reactjs - 未捕获错误 : Actions must be plain objects. 相反,实际类型为: 'undefined'

标签 reactjs redux react-redux redux-thunk

我正在构建一个 Web 应用程序 Reactjs 和 Redux,您可以在其中登录仪表板,然后可以注销。 我使用“redux-react-session”来管理 session ,并在 userActions.js 中创建了一个名为 logoutUser 的操作。此操作在 Dashboard.jsx 中导入并调用,我在其中添加了带有 onClick 事件的注销按钮。我期望的是,当我点击注销时,它应该只是将我重定向到主页('/')

我的问题是:每当我单击注销按钮时,它会将我重定向到主页(“/”),然后刷新回仪表板。

控制台.dev 错误:

Uncaught Error: Actions must be plain objects. Instead, the actual type was: 'undefined'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions.

userAction.js:

import axios from 'axios'
import { sessionService } from 'redux-react-session'


export const loginUser = (credentials, navigate, setFieldError, setSubmitting) => {
    //make checks and get some data
    return () => {
        axios.post("http://localhost:5000/api/form/signin",
            credentials,
            {
                headers: {
                    "Content-Type": "application/json"
                }
            }
        ).then((response) => {
            const { data } = response;

            if (data.status === "FAILED") {
                const { message } = data;

                //check for specific error
                if (message.includes("credentials")) {
                    setFieldError("email", message);
                    setFieldError("password", message);
                } else if (message.includes("password")) {
                    setFieldError("password", message);
                    alert("Wrong email or password")
                }
            } else if (data.status === "SUCCESS") {
                const userData = data.data;
                const token = userData._id;

                sessionService.saveSession(token)
                    .then(() => {
                        sessionService.saveUser(userData)
                            .then(() => {
                                if (userData.role === "admin") {
                                    navigate("/dashboard")
                                } else if (userData.role === "user") {
                                    navigate("/joblist")
                                } else {
                                    navigate("/")
                                    alert("Role is not known, please contact Admin or make a new account!")
                                    sessionService.deleteSession(token)
                                }
                            })
                            .catch(err => console.error(err))
                    })
                    .catch(err => console.error(err))
            }
        })
            .catch(err => { console.error(err) });
    }
}




export const signupUser = (credentials, navigate, setFieldError, setSubmitting) => {
    return (dispatch) => {

        axios.post("http://localhost:5000/api/form/signup",
            credentials,
            {
                headers: {
                    "Content-Type": "application/json"
                }
            }
        ).then((response) => {
            const { data } = response;

            if (data.status === 'FAILED') {
                const { message } = data;
                //check for specific error
                if (message.includes("name")) {
                    setFieldError("name", message);
                } else if (message.includes("email")) {
                    setFieldError("email", message);
                } else if (message.includes("date")) {
                    setFieldError("dateOfBirth", message);
                } else if (message.includes("password")) {
                    setFieldError("password", message);
                }

                //complete submission
                setSubmitting(false);

            } else if (data.status === "SUCCESS") {
                //Login user after successfull signup
                const { email, password } = credentials;

                dispatch(loginUser({ email, password }, navigate, setFieldError, setSubmitting))
            }
        })
            .catch(err => console.error(err));
    }
}




export const logoutUser = (navigate) => {
    return () => {
        sessionService.deleteSession();
        sessionService.deleteUser();
        navigate('/');
    }
}

仪表板.jsx:

import {
  StyledTitle,
  StyledSubTitle,
  Avatar,
  StyledButton,
  ButtonGroup,
  StyledFormArea,
  colors
} from './../../components/Styles';

//Formik
import { Formik, Form } from 'formik';
import { TextInput } from '../../components/FormLib';

//Logo
import Logo from './../../assets/logo.png';

//auth & redux
import { connect } from 'react-redux';
import { logoutUser } from '../../auth/actions/userActions';

//React router
import { useNavigate } from 'react-router-dom';

import { useDispatch } from 'react-redux';




const Dashboard = ({ logoutUser }) => {
  const navigate = useNavigate();
  const dispatch = useDispatch();

  return (
    <div>
      <div>
        <Avatar image={Logo} />
      </div>
      <StyledFormArea bg={colors.dark2} style={{ marginLeft: '-400px' }}>
        <StyledTitle size={65}>Welcome to Dashboard </StyledTitle>
        <StyledSubTitle size={27}>Feel free to post new jobs</StyledSubTitle>
        <Formik
          initialValues={{
            title: '',
            description: ''
          }}
        >
          <Form>
            <TextInput
              name="title"
              type="text"
              label="Title :"
              placeholder="Job Title"
            />
            <TextInput
              name="description"
              type="text"
              label="Description :"
              placeholder="Job description"
            />
          </Form>
        </Formik>
        <ButtonGroup>
          <StyledButton to="#">Post Job</StyledButton>
          <StyledButton
            bg={colors.red}
            to="#"
            onClick={()=> dispatch(logoutUser(navigate))}
          >
            Logout
          </StyledButton>
        </ButtonGroup>
      </StyledFormArea>
    </div>
  );
};

export default connect(null, { logoutUser })(Dashboard);

Store.js:

import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/rootReducer';

import {sessionService} from 'redux-react-session';

const initialState = {}
const middlewares = [thunk]

const store = createStore(rootReducer, initialState, compose(applyMiddleware(...middlewares)));
sessionService.initSessionService(store);

export default store;

编辑:

如果我在操作中添加return():

export const logoutUser = (navigate) => {
    return () => {
        sessionService.deleteSession();
        sessionService.deleteUser();
        navigate('/');
    }
}

我在 Console.dev 中收到此错误:

Uncaught TypeError: logoutUser(...) is not a function

编辑(2):

删除 logoutUser(navigate)( )return ( ) => { ... }( ) code> 将保持页面刷新然后让我返回仪表板

最佳答案

问题似乎与使用connect高阶组件手动向商店分派(dispatch)操作有关。 connect HOC 自动将操作创建者包装在对 dispatch 的调用中,并将可分派(dispatch)操作作为 prop 注入(inject)到装饰组件中。然后,您的代码将 logoutUser 包装在对 dispatch 的另一个调用中。选择其中之一,但不能同时选择两者。

使用连接 HOC

//auth & redux
import { connect } from 'react-redux';
import { logoutUser } from '../../auth/actions/userActions';

//React router
import { useNavigate } from 'react-router-dom';
...

const Dashboard = ({ logoutUser }) => {
  const navigate = useNavigate();

  return (
    <div>
      ...
      <StyledFormArea bg={colors.dark2} style={{ marginLeft: '-400px' }}>
        <StyledTitle size={65}>Welcome to Dashboard </StyledTitle>
        <StyledSubTitle size={27}>Feel free to post new jobs</StyledSubTitle>
        ...
        <ButtonGroup>
          <StyledButton to="/">Post Job</StyledButton>
          <StyledButton
            bg={colors.red}
            to="/" // <-- fix target from "#" to "/"
            onClick={(e) => {
              e.preventDefault(); // <-- prevent any default button actions
              logoutUser(navigate); // <-- call decorated action
            }}
          >
            Logout
          </StyledButton>
        </ButtonGroup>
      </StyledFormArea>
    </div>
  );
};

const mapDispatchToProps = {
  logoutUser
};

export default connect(null, mapDispatchToProps)(Dashboard);

使用useDispatch钩子(Hook)

//auth & redux
import { useDispatch } from 'react-redux';
import { logoutUser } from '../../auth/actions/userActions';

//React router
import { useNavigate } from 'react-router-dom';
...

const Dashboard = () => {
  const navigate = useNavigate();
  const dispatch = useDispatch();

  return (
    <div>
      ...
      <StyledFormArea bg={colors.dark2} style={{ marginLeft: '-400px' }}>
        <StyledTitle size={65}>Welcome to Dashboard </StyledTitle>
        <StyledSubTitle size={27}>Feel free to post new jobs</StyledSubTitle>
        ...
        <ButtonGroup>
          <StyledButton to="/">Post Job</StyledButton>
          <StyledButton
            bg={colors.red}
            to="/" // <-- fix target from "#" to "/"
            onClick={(e) => {
              e.preventDefault(); // <-- prevent any default button actions
              dispatch(logoutUser(navigate)); // <-- dispatch action
            }}
          >
            Logout
          </StyledButton>
        </ButtonGroup>
      </StyledFormArea>
    </div>
  );
};

export default Dashboard;

错误消息似乎还表明您尚未将任何异步操作中间件添加到您的 redux 存储中。

You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions.

我建议按照 reduxreact-redux 指南将其添加到您的商店配置中,或更新到 redux-toolkit (< em>相同的维护者),其中包括开箱即用的 thunk 中间件,因为它是一个常见的用例。

关于reactjs - 未捕获错误 : Actions must be plain objects. 相反,实际类型为: 'undefined',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72645333/

相关文章:

reactjs - [Webpack][React] 在带有代码拆分的 SSR 上,我如何获得页面所需的 block 列表?

javascript - 如何让 React 和 Meteor 结合在一起

javascript - 当路线改变时 react 获取数据

reactjs - React-Redux connect() 和错误 TS2345

javascript - React redux - 应用 "fetch"并在调度内部调度失败

reactjs - FirebaseUI react : allow user to sign up via email+password without providing their first/last name

reactjs - 类组件的 useEffect 替代

javascript - 如何从 redux-observable 调度多个 Action ?

testing - 我正在努力测试这个 redux saga

javascript - ReactJS - 为调度操作的函数创建一个单独的文件