繁体   English   中英

为什么我必须第二次发送操作来更新 state? Redux,反应,NodeJS,MySQL

[英]Why do I have to dispatch an action a second time to update state? Redux, React, NodeJS, MySQL

所以我正在使用 MySQL、Node、React 和 Redux 创建一个用户身份验证登录系统。

我创建了一个注册端点来将注册用户插入到我的 MySQL 数据库中。

然后,我为 select 从我的 MySQL 数据库中登录的用户创建了一个登录端点。

我的文件夹结构是什么样的

SignUpAndSignInComponent 只返回我的 SignUp 和 SignIn 组件。

我的注册组件使用 Hooks 设置本地 state,然后通过我的注册端点传递到我的后端。

我的 SignIn 组件还使用 Hooks 设置本地 state 然后传递到我的后端以从数据库中检索用户。

此时我只显示了登录的用户,但只显示在 SignUpAndSignIn 页面上。

我希望能够显示登录到我的 header 的用户,所以我需要提升 state。

我现在所做的是将 Redux 引入到一个大商店中来管理我的 state 并保持登录用户的 state 以便所有组件都可以访问它。

我的 SignIn 组件使用 useDispatch 将操作分派到商店

我希望能够从登录用户那里获取用户名,并将其传递到我最顶层应用程序级别的 header 组件中。

我最顶层的 App 组件使用 useSelector 检索从商店登录的用户并将其显示在我的 header 中。

我设法通过创建我的商店、rootReducer、用户减速器、操作和类型来做到这一点。 我什至保留了 state 以便使用 redux-persist 和本地存储在应用程序级别可见。

我的问题是,当用户登录(单击登录表单上的提交按钮)时,它只会更新本地 state 并在其本地视图上显示 state。

只有当我第二次登录时,我的操作才会发送到商店,以便我可以在顶层查看登录用户。 为什么是这样?

我需要使用 Redux-thunk 来处理这个异步请求吗? 这是我认为我应该做的,但需要更清楚地了解实际问题是什么。

这是我的 redux 记录器中间件在控制台中发布的内容

本地视图,即登录组件中的视图

全局视图,即 App.js 组件中的视图

带调度的登录组件表单

``` import React, { useState, useCallback } from 'react';
import Axios from 'axios';
import { useSelector, useDispatch } from 'react-redux';
import { setCurrentUser } from '../../redux/user/user.reducer';
import { Form, Button } from 'react-bootstrap';
import { UserActionTypes } from '../../redux/user/user.types';

const SignIn = () => {

   const dispatch = useDispatch();

   const [emailLog, setEmailLog] = useState('');
   const [passwordLog, setPasswordLog] = useState('');

   const [loginStatus, setLoginStatus] = useState('');
   
   const CallBoth = async () => {
      await login();
      isUserLoggedIn();
   }

   const login = async () => {
      await Axios.post('http://localhost:3001/login', {
         email: emailLog,
         password: passwordLog 
      }).then((response) => {
         
         if (response.data.message) {
            setLoginStatus(response.data.message);
         } else {
            setLoginStatus(response.data[0].username);
         }

      });
   }

   const isUserLoggedIn = () => {
      dispatch({type: UserActionTypes.SET_CURRENT_USER,
         payload: loginStatus
      });
   }



   window.onbeforeunload = function (e) {
      console.log(e);
      return true;
  }

      return(
         <div className="sign-in">
         <Form onSubmit={CallBoth} >
               <Form.Group controlId="formBasicEmail">
                  <Form.Label>Email address</Form.Label>
                  <Form.Control type="email" placeholder="Enter email"
                  onChange={
                     (e) => {setEmailLog(e.target.value);
                     }}
                  />
                  <Form.Text className="text-muted">
                     We'll never share your email with anyone else.
                  </Form.Text>
               </Form.Group>
   
               <Form.Group controlId="formBasicPassword">
                  <Form.Label>Password</Form.Label>
                  <Form.Control type="password" placeholder="Password"
                  onChange={
                     (e) => {setPasswordLog(e.target.value);
                     }}
                  />
               </Form.Group>
               <Form.Group controlId="formBasicCheckbox">
                  <Form.Check type="checkbox" label="Check me out" />
               </Form.Group>
               <Button variant="primary" type="submit"
               
               >
                  Submit
               </Button>
           </Form>
           <h1>{loginStatus}</h1>
         </div>
      )
}

export default SignIn; ```

带有 useSelector 的 App.js

```import React, { Component, useEffect } from 'react';
import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

import HomePage from './pages/homepage/homepage.component';
import SignUpAndSignIn from './pages/signupandsignin/signupandsignin.component';
import Header from './components/header/header.component';

import { useSelector, useDispatch } from 'react-redux';

function App() {

  const userLoggedIn = useSelector( (state) => state.user.currentUser);

  

  return (
    <>
    <Router>
      <Header />
      <h1>User logged in: {userLoggedIn}</h1>
      <Switch>
        <Route exact path="/" component={HomePage}>
          <HomePage />
        </Route>
        <Route path="/signupandsignin" component={SignUpAndSignIn}>
          <SignUpAndSignIn />
        </Route>
      </Switch>
    </Router>



    </>
  );
}
export default App;


看起来这是由于您在登录 function 中进行异步调用的方式。 You have used async await but you are also using.then() Your CallBoth function is waiting for the login function to finish, but it is not waiting for the.then call to finish, so the isUserLoggedIn is dispatching the state before it gets updated - 它在第二次登录时被捕获,这就是它在第二次登录尝试后工作的原因。

您可以简单地删除 CallBoth function 并将 isUserLoggedIn 调用放在 setState 调用之后的.then 中。 然后登录 function 将是您的提交处理程序

已编辑

这样做,您不需要登录 function 来异步等待。 此外,您实际上不需要将响应数据设置为 state - 您可以将其直接传递给 isUserLoggedIn。 由于您将数据存储在 redux 存储中,因此您根本不需要将其存储在组件 state 中。 只需使用您的选择器从 redux 中抓取它

您可以修改您的 isUserLoggedIn function 以接受响应数据作为参数,如下所示:

const isUserLoggedIn = (loginStatus) => {
      dispatch({type: UserActionTypes.SET_CURRENT_USER,
         payload: loginStatus
      });
   }

然后在您的登录 function 中:

const login = () => {
      Axios.post('http://localhost:3001/login', {
         email: emailLog,
         password: passwordLog 
      }).then((response) => {
         
         if (response.data.message) {
            isUserLoggedIn(response.data.message);
         } else {
            isUserLoggedIn(response.data[0].username);
         }

      });
   }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM