所以我浪费了5个小时。
我有一个像这样的redux thunk动作:
export const fetchUser = () => async (getState, dispatch) => {
if (getIsFetching(getState().user)) {
return Promise.resolve();
}
dispatch(fetchUserRequest());
try {
const response = await api.fetchUser();
dispatch(fetchUserSuccess({ userObject: { ...response } }));
} catch (error) {
dispatch(fetchUserFailure({ message: "Could not fetch user profile." }));
}
};
调用此内容始终以“操作必须是普通对象。使用自定义中间件进行异步操作。”。
是的,当然。我已经在使用redux-thunk了,为什么它一直在烦我?注意:fetchUserRequest(),fetchUserSuccess()和fetchUserFailure()都返回简单的普通redux动作。
答案 0 :(得分:7)
了解此错误消息是了解Redux世界中许多内容的关键。这甚至可能是您将来会问到的面试问题。
实际上,您的动作创建者有两个错误。动作创建者的第一件事是,您的动作创建者应该返回带有type
属性和可选的payload
属性的普通JavaScript对象,但是目前您不从以下位置返回操作您的动作创建者。
您可能会看到代码编辑器,然后是动作创建者,并且您可能正在思考,您是否正在寻找与我相同的动作创建者?看起来您正在返回具有type
属性的对象,但实际上您没有。
即使看起来您正在返回一个JavaScript对象,事实并非如此。
我们在编辑器中编写的许多代码是ES2015、2016、2017、2018等。您和我编写的代码都将转换为es2015语法,这就是在浏览器内部实际执行的代码。
因此,即使此函数看起来像返回带有type
属性的对象,实际上,在将其转换为es2015代码后,我们却没有。
下次将您的异步操作创建者拖放到babeljs.io中,您将明白我的意思。
这实际上是将我们的代码向下移植到ES2015。
因此,在代码编辑器内部,您认为自己正在执行编写的代码,但是实际上,因为您特别具有这种async / await语法,所以整个功能都扩展到了babeljs右侧。 io。
因此,当我告诉您动作创建者没有返回普通的JavaScript对象时,它的原因是您具有异步/等待语法。这就是为什么您的动作创建者无法按预期工作的原因。
因此,您最初返回的不是您的操作对象,而是您返回的对象。因此,当您的动作创建者第一次被调用时,您不会返回该动作对象,而是如您所见,在其中包含了一些代码来返回您的请求对象。这就是返回请求的内容。因此,您从动作创建者那里返回了请求,该请求进入了store.dispatch
方法中。
然后,redux存储查看返回的内容并说好,这是仅具有type
属性的普通JaavaScript对象吗?好吧,在这种情况下,不,因为我们只是返回了请求对象,所以没有返回操作,这就是为什么我们最终看到讨厌的红色消息,说操作必须是普通对象。因此,我们没有返回普通对象,而操作必须返回普通对象。我们返回了一个可能分配了一些奇特方法的请求对象,并且可能没有type
属性,因此我们绝对没有分派我们认为分派的东西。
所有这些都是由于您使用的async / await语法。
因此,这是您的动作创建者的第一个问题。由于使用了被转换为es5代码的async / await语法,因此,您在浏览器中实际运行的不是您实际认为的运行。
因此,我们正在调度NOT Redux动作,正在调度Redux不关心的随机对象。
那么,我们如何正确利用称为Redux-Thunk的中间件?在我们回答这个问题之前,让我们了解一下Redux世界中的中间件。
中间件是普通的JavaScript函数,将在我们分派的每个操作中调用该中间件。在该功能内部,中间件可以阻止调度动作,阻止其进入任何化简器,修改动作或以任何方式,形状或形式操纵动作。
Redux-Thunk是最受欢迎的中间件,因为它可以帮助我们与异步动作创建者一起工作。
好的,那么Redux-Thunk如何帮助我们解决这个问题?
好吧,Redux-Thunk将放宽常规的动作创建者规则或Redux,就像我在上面所说的那样,动作创建者必须返回动作对象,它必须具有type
属性,并且可选地, payload
属性。
Redux-Thunk没有内在的本质,它允许我们做很多事情,其中之一是处理动作创建者,但这不是其主要目的。
一旦动作创建者参与到Redux-Thunk中,它就可以返回普通对象或返回函数。
你知道这是怎么回事吗?
那么返回函数有什么帮助?
因此,我们的动作创建者以对象或函数的形式返回“动作”。该“动作”将被发送到调度功能,最终将在Redux-Thunk内部结束。
Redux-Thunk会说:“嗨,行动,您是函数还是对象?如果“操作”告诉Redux-Thunk它的对象,则Redux-Thunk会说:“好吧,感谢您停止操作,但是我更喜欢只处理函数”,然后Redux-Thunk会将“ action”推向减速器。
否则,Redux-Thunk会说:“哦,您是一个函数吗?不错!”然后,Redux-Thunk将调用您的函数,并将dispatch
,getState
函数作为参数传递。您已经获得答案的语法版本,所以请允许我提供它的一个变体。
因此,不仅如此:
export const fetchPosts = async () => {
const response = await jsonPlaceholder.get('/posts');
return {
type: 'FETCH_POSTS',
payload: response
}
};
使用Redux-Thunk,您将包括以下内容:
export const fetchPosts = async () => {
return function(dispatch, getState) {
const response = await jsonPlaceholder.get('/posts');
return {
type: 'FETCH_POSTS',
payload: response
}
}
};
现在在上面的示例中,我正在使用动作创建者向外部API发出异步请求。因此,dispatch
具有在我们应用程序的Redux端更改数据的无限权力。
您看到我利用getState
,因此您也可以理解,除了dispatch
之外,getState
还将返回商店中的所有数据。这两个参数在Redux应用程序内部具有无限的功能。通过dispatch
,我们可以更改所需的任何数据;通过getState
,我们可以读取所需的任何数据。
转到Redux-Thunk本身的源代码: https://github.com/reduxjs/redux-thunk/blob/master/src/index.js
以上是Redux-Thunk的全部内容。只有6到7行可以执行任何操作,其他则是初始化步骤,函数声明和导出。第2行是一系列返回函数的函数。
在它的主体中,您看到发生了什么的逻辑并询问它,您是否调度并执行了操作,如果是,则是操作还是功能?
我上面描述的所有内容都被捕获在源代码中。
因此,对于我来说,正确地将Redux-Thunk应用于我给您的示例中,我将转到根index.js
文件,然后将其安装在终端中后将其导入,如下所示:
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore, applyMiddleware } from "redux";
import thunk from 'redux-thunk';
import App from "./components/App";
import reducers from "./reducers";
ReactDOM.render(
<Provider store={createStore(reducers)}>
<App />
</Provider>,
document.querySelector("#root")
注意,我也导入了applyMiddleware
。此功能是我们将中间件连接到Redux的方式。
因此,然后我将createStore
预先应用到名为store
的变量中,并在Provider存储中实现该变量,如下所示:
const store = createStore(reducers);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#root")
);
要连接Redux-Thunk,作为第二个参数,我将调用applyMiddleware
并像这样传递thunk
:
const store = createStore(reducers, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.querySelector("#root")
然后在动作创建者内部进行一两个更改。我仍然可以返回带有类型属性的普通对象,这是一个选择,使用Redux-Thunk,我们仍然可以使普通的动作创建者返回对象,但是我们不必返回动作。
因此,除了返回操作之外,我可以调用dispatch
并像这样传递我的操作对象:
export const fetchPosts = () => {
return async function(dispatch, getState) {
const response = await jsonPlaceholder.get('/posts');
dispatch({type: 'FETCH_POSTS', payload: response })
}
};
在Redux-Thunk中,我们可以使用async / await语法,因为该语法只会修改内部函数的返回值。该功能一无所获。 Redux-Thunk将无法获得返回内容的引用并加以利用,我们可以返回还是不返回,我们从外部函数返回的内容就是我们所关心的。
重构上面我刚刚分享的内容的一种常见方式如下:
export const fetchPosts =()=> { 返回异步(发送)=> { const response = await jsonPlaceholder.get('/ posts');
dispatch({type: 'FETCH_POSTS', payload: })
} };
因此,如果您不使用函数中的getState
,则可以将其作为参数保留。您可以像这样使代码更简洁:
export const fetchPosts = () => async dispatch => {
const response = await jsonPlaceholder.get('/posts');
dispatch({type: 'FETCH_POSTS', payload: response })
}
您将在很多Redux项目中看到这一点。就是这样。
答案 1 :(得分:5)
export const fetchUser = () => async (getState, dispatch) => {// your code here}
需要
export const fetchUser = () => async (dispatch, getState) => {// your code here}
(getState,dispatch)!==(发送, getState )
虽然我很高兴我最终解决了这个问题,但那里的方式非常令人沮丧。