“Actions must be plain objects. Use custom middleware for async actions.” 即使应用了thunk

huangapple go评论60阅读模式
英文:

"Actions must be plain objects. Use custom middleware for async actions." Even though thunk is applied

问题

I'll provide translations for the code snippets you provided:

applying thunk

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import logger from 'redux-logger';
import { composeWithDevTools } from 'redux-devtools-extension';

import rootReducer from './services/reducers/root_reducer.js';

// Apply logger middleware only in the dev environment.
const composedEnhancer = compose(composeWithDevTools(), applyMiddleware(thunk, logger.default));

const configureStore = (preloadedState = {}) => {
  return createStore(rootReducer, preloadedState, composedEnhancer);
};

export default configureStore;

index.js

import './assets/styles/reset.css';
import './assets/styles/index.css';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import configureStore from './store.js';
import App from './app.js';

const rootElement = document.getElementById('root');
const root = ReactDOM.createRoot(rootElement);
root.render(
  <Router>
    <Provider store={configureStore()}>
      <App />
    </Provider>
  </Router>
)

thunk action

import UserApi from '../api/user_api.js';
import { UserActions } from '../constants/actions.js';
import { displayError } from './error_actions.js';
import userPool from '../../user_pool.js';

export const addUser = (user) => {
  return {
    type: UserActions.addUser,
    payload: user
  };
};

export const register = (registerData) => {
  return async (dispatch) => {
    userPool.signUp(registerData.email, registerData.password, registerData.attributeList, null, (error, result) => {
      if (error) {
        return dispatch(displayError(error));
      } else {
        return dispatch(addUser(result.user));
      }
    });
  }
}

dispatching thunk action

// Called when the form is submitted
const onSubmit = (event) => {
  event.preventDefault();
  const registerParams = {
    email: user.email,
    password: user.password,
    attributeList: []
  };
  delete user.email;
  delete user.password;
  Object.entries(user).forEach(([key, value]) => {
    if (key === "birthdate") {
      const options = { year: 'numeric', month: '2-digit', day: '2-digit' };
      value = value.toLocaleDateString('en-US', options).replace(/\//g, '-');
    }
    let attributeParams = {
      Name: key,
      Value: value
    };
    let cognitoAttribute = new CognitoUserAttribute(attributeParams);
    registerParams.attributeList.push(cognitoAttribute);
  });
  dispatch(register(registerParams));
};
英文:

I am trying to figure out why I am getting "Actions must be plain objects. Use custom middleware for async actions." when I dispatch my thunk action. From my understanding, the normal cause of this issue would be that my thunk middleware wasn't applied, and thus redux only expects POJO actions, instead of thunk actions. However, unless I am mistaken, I believe I have already done that. I also double checked to make sure that my thunk action creator was, in fact, returning a thunk action. I have looked around, and even asked chatgpt, and I can't seem to find any solution to this issue. Here is the code:

applying thunk

import { createStore, applyMiddleware, compose } from &#39;redux&#39;;
import thunk from &#39;redux-thunk&#39;;
import logger from &#39;redux-logger&#39;;
import { composeWithDevTools } from &#39;redux-devtools-extension&#39;;

import rootReducer from &#39;./services/reducers/root_reducer.js&#39;;

// apply logger middleware only in dev env
const composedEnhancer = compose(composeWithDevTools(), applyMiddleware(thunk, logger.default));

const configureStore = (preloadedState = {}) =&gt; {
  return createStore(rootReducer, preloadedState, composedEnhancer);
};

export default configureStore;

index.js

import &#39;./assets/styles/reset.css&#39;;
import &#39;./assets/styles/index.css&#39;;
import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom/client&#39;;
import { Provider } from &#39;react-redux&#39;;
import { BrowserRouter as Router } from &#39;react-router-dom&#39;;
import configureStore from &#39;./store.js&#39;;
import App from &#39;./app.js&#39;;

const rootElement = document.getElementById(&#39;root&#39;);
const root = ReactDOM.createRoot(rootElement);
root.render(
  &lt;Router&gt;
    &lt;Provider store={configureStore()}&gt;
      &lt;App /&gt;
    &lt;/Provider&gt;
  &lt;/Router&gt;
)

thunk action

import UserApi from &#39;../api/user_api.js&#39;;
import { UserActions } from &#39;../constants/actions.js&#39;;
import { displayError } from &#39;./error_actions.js&#39;;
import userPool from &#39;../../user_pool.js&#39;;

export const addUser = (user) =&gt; {
  return {
    type: UserActions.addUser,
    payload: user
  };
};

export const register = (registerData) =&gt; {
  return async (dispatch) =&gt; {
    userPool.signUp(registerData.email, registerData.password, registerData.attributeList, null, (error, result) =&gt; {
      if (error) {
        return dispatch(displayError(error));
      } else {
        return dispatch(addUser(result.user));
      }
    });
  }
}

dispatching thunk action

  // Called when form is submitted
  const onSubmit = (event) =&gt; {
    event.preventDefault();
    const registerParams = {
      email: user.email,
      password: user.password,
      attributeList: []
    };
    delete user.email;
    delete user.password;
    Object.entries(user).forEach(([key, value]) =&gt; {
      if (key === &quot;birthdate&quot;) {
        const options = { year: &#39;numeric&#39;, month: &#39;2-digit&#39;, day: &#39;2-digit&#39; };
        value = value.toLocaleDateString(&#39;en-US&#39;, options).replace(/\//g, &#39;-&#39;);
      }
      let attributeParams = {
        Name: key,
        Value: value
      };
      let cognitoAttribute = new CognitoUserAttribute(attributeParams);
      registerParams.attributeList.push(cognitoAttribute);
    });
    dispatch(register(registerParams));
  };

答案1

得分: 2

One potential issue is that this line looks wrong:

const composedEnhancer = compose(composeWithDevTools(), applyMiddleware(thunk, logger.default));

There's three problems in that one line:

  • applyMiddleware should always be first
  • if you're adding the devtools setup it should be last
  • but actually composeWithDevtools() replaces the standalone compose

so it really should be:

const composedEnhancer = composeWithDevtools(applyMiddleware(thunk, logger.default));

I'm not certain that is the cause of the error, but there's a good chance.

HOWEVER, there's another issue:

The style of Redux logic you're writing is very outdated.

Today, all Redux users should be using our official Redux Toolkit package to write their Redux code. It drastically simplifies Redux usage, and that includes some of the code in your example.

In particular, RTK's configureStore API sets up the store with the thunk middleware and Redux DevTools already configured, and RTK's createAsyncThunk will do the "dispatch actions for an async request" work for you.

See our docs for details on how to use Redux Toolkit:

英文:

One potential issue is that this line looks wrong:

const composedEnhancer = compose(composeWithDevTools(), applyMiddleware(thunk, logger.default));

There's three problems in that one line:

  • applyMiddleware should always be first
  • if you're adding the devtools setup it should be last
  • but actually composeWithDevtools() replaces the standalone compose

so it really should be:

const composedEnhancer = composeWithDevtools(applyMiddleware(thunk, logger.default));

I'm not certain that is the cause of the error, but there's a good chance.

HOWEVER, there's another issue:

The style of Redux logic you're writing is very outdated.

Today, all Redux users should be using our official Redux Toolkit package to write their Redux code. It drastically simplifies Redux usage, and that includes some of the code in your example.

In particular, RTK's configureStore API sets up the store with the thunk middleware and Redux DevTools already configured, and RTK's createAsyncThunk will do the "dispatch actions for an async request" work for you.

See our docs for details on how to use Redux Toolkit:

答案2

得分: 1

以下是翻译好的部分:

“onSubmit”回调通过使用“register()”的返回值调用“dispatch()”。来自“register()”的返回值是一个Promise,而不是可序列化的对象。

“register()”通过“dispatch()”的返回值来履行(解析),后者也返回一个Promise。

英文:

...what @markerikson said plus...

The onSubmit callback makes a call to dispatch() with the return from register() as its argument. The return from register() is a Promise, not a serializable object.

register() fulfills (resolves) with the return value from dispatch() which returns a Promise.

huangapple
  • 本文由 发表于 2023年5月7日 11:18:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76192053.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定