簡體   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