【问题标题】:Cannot read property 'then' of undefined on Redux thunk action无法读取 Redux thunk 操作上未定义的属性“then”
【发布时间】:2019-01-29 13:44:47
【问题描述】:

我是 React、Redux 和 Thunk 的新手,并且一直在关注有关该主题的教程,最近一次是在 Pluralsight 上的 Building Applications with React and Redux in ES6。我一直在将本教程从 ES6 转换为 Typescript 3,并将 React v3 转换为 React v4。

我遇到了很多我能够按照这种模式解决的问题(尤其是与路由相关的任何问题),但我遇到了一个我无法解决的问题。从各种来源 inc Cannot read property '.then' of undefined when testing async action creators with redux and react 听起来我不能在返回 void 的函数上使用 .then() 函数,但它是一个(应该)返回承诺的异步函数,但是智能感知另有说明。代码如下。

saveCourse = (event: any) => {
    event.preventDefault();
    this.props.actions.saveCourse(this.state.course)
        .then(this.setState({ fireRedirect: true }));
}

上面的代码在我的组件上,props.actions通过mapStateToProps连接到redux store,这个函数在按钮点击时被调用。这是上面这个函数上的.then() 出错了。这个函数调用下面的动作

export const saveCourse = (course: Course) => {
    return (dispatch: any, getState: any) => {
        dispatch(beginAjaxCall());
        courseApi.saveCourse(course).then((savedCourse: Course) => {
            course.id ? dispatch(updateCourseSuccess(savedCourse)) :
                dispatch(createCourseSuccess(savedCourse));
        }).catch(error => {
            throw (error);
        });
    }
}

上面的动作是asyc调用,这里的.then()没有报错但是VS Code说整个saveCourse函数返回void。

从本教程来看,确实没有任何差异应该有所作为(箭头函数而不是常规等......)所以我想知道我缺少的版本之间是否存在模糊的变化,但不确定在哪里看,可能缺少一些基本的东西。谁能明白为什么我不能在saveCourse() 函数上执行.then()

如果您需要更多信息,请告诉我。

编辑:courseApi 只是一个模拟 api,代码如下。

import delay from './delay';

const courses = [
{
    id: "react-flux-building-applications",
    title: "Building Applications in React and Flux",
    watchHref: "http://www.pluralsight.com/courses/react-flux-building-applications",
    authorId: "cory-house",
    length: "5:08",
    category: "JavaScript"
},
{
    id: "clean-code",
    title: "Clean Code: Writing Code for Humans",
    watchHref: "http://www.pluralsight.com/courses/writing-clean-code-humans",
    authorId: "cory-house",
    length: "3:10",
    category: "Software Practices"
},
{
    id: "architecture",
    title: "Architecting Applications for the Real World",
    watchHref: "http://www.pluralsight.com/courses/architecting-applications-dotnet",
    authorId: "cory-house",
    length: "2:52",
    category: "Software Architecture"
},
{
    id: "career-reboot-for-developer-mind",
    title: "Becoming an Outlier: Reprogramming the Developer Mind",
    watchHref: "http://www.pluralsight.com/courses/career-reboot-for-developer-mind",
    authorId: "cory-house",
    length: "2:30",
    category: "Career"
},
{
    id: "web-components-shadow-dom",
    title: "Web Component Fundamentals",
    watchHref: "http://www.pluralsight.com/courses/web-components-shadow-dom",
    authorId: "cory-house",
    length: "5:10",
    category: "HTML5"
}
];

function replaceAll(str: any, find: any, replace: any) {
return str.replace(new RegExp(find, 'g'), replace);
}

//This would be performed on the server in a real app. Just stubbing in.
const generateId = (course: any) => {
return replaceAll(course.title, ' ', '-');
};

class CourseApi {
static getAllCourses() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(Object.assign([], courses));
        }, delay);
    });
}

static saveCourse(course: any) {
    course = Object.assign({}, course); // to avoid manipulating object         passed in.
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            // Simulate server-side validation
            const minCourseTitleLength = 1;
            if (course.title.length < minCourseTitleLength) {
                reject(`Title must be at least ${minCourseTitleLength} characters.`);
            }

            if (course.id) {
                const existingCourseIndex = courses.findIndex(a => a.id == course.id);
                courses.splice(existingCourseIndex, 1, course);
            } else {
                //Just simulating creation here.
                //The server would generate ids and watchHref's for new courses in a real app.
                //Cloning so copy returned is passed by value rather than by reference.
                course.id = generateId(course);
                course.watchHref = `http://www.pluralsight.com/courses/${course.id}`;
                courses.push(course);
            }

            resolve(course);
        }, delay);
    });
}

static deleteCourse(courseId: any) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            const indexOfCourseToDelete = courses.findIndex(course =>
                course.id == courseId
            );
            courses.splice(indexOfCourseToDelete, 1);
            resolve();
        }, delay);
    });
}
}

export default CourseApi;

【问题讨论】:

  • 尝试添加返回方法,如return courseApi.saveCourse(course).then.... ,如果不起作用,你能显示courseApi
  • 第一个代码块中的 .then() 失败,抱歉没有说清楚。现在编辑并上传 courseApi
  • @SGhaleb 已编辑,.then() 在第一块代码中失败,抱歉我真的没有说清楚
  • 第一个解决方案没有解决问题,你尝试的时候返回了什么错误?
  • @SGhaleb 我尝试按照您的建议将操作包装在一个承诺中,但收到错误“错误:操作必须是普通对象。使用自定义中间件进行异步操作。”。这是一个混蛋,这真的应该有效。你能编辑thunk的中间件吗?我的 createStore 函数中有“applyMiddleware(thunk, reduxImmutableStateInvariant())”

标签: reactjs redux react-redux redux-thunk


【解决方案1】:

您遇到的问题是您没有在 Action Creator 中返回承诺。

在这里查看 redux-thunk 的示例:

https://github.com/reduxjs/redux-thunk

function makeASandwichWithSecretSauce(forPerson) {

  // Invert control!
  // Return a function that accepts `dispatch` so we can dispatch later.
  // Thunk middleware knows how to turn thunk async actions into actions.

  return function (dispatch) {
    return fetchSecretSauce().then(
      sauce => dispatch(makeASandwich(forPerson, sauce)),
      error => dispatch(apologize('The Sandwich Shop', forPerson, error))
    );
  };
}

他们正在返回由fetchSecretSauce 生成的承诺。

您需要在示例中类似地返回承诺:

export const saveCourse = (course: Course) => {
    return (dispatch: any, getState: any) => {
        dispatch(beginAjaxCall());
        return courseApi.saveCourse(course).then((savedCourse: Course) => {
            course.id ? dispatch(updateCourseSuccess(savedCourse)) :
                dispatch(createCourseSuccess(savedCourse));
        }).catch(error => {
            throw (error);
        });
    }
}

【讨论】:

    【解决方案2】:

    除了在 Action Creator 中返回承诺之外,您可能还想确保在您的 mapDispatchToProps 中,映射的相应函数应声明为 async。可能是await里面的dispatch语句。

    我遇到了类似的问题,错误消息完全相同,但除了返回承诺之外,这是我一开始没有注意到的其他问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-21
      • 1970-01-01
      • 2019-08-19
      • 1970-01-01
      • 2016-10-23
      • 2014-09-07
      相关资源
      最近更新 更多