Почему я должен отправлять действие во второй раз, чтобы обновить состояние? Redux, React, NodeJS, MySQL

#javascript #reactjs #react-redux #axios #redux-thunk

#javascript #reactjs #react-redux #axios #redux-thunk

Вопрос:

Итак, я создаю систему авторизации пользователя с использованием MySQL, Node, React и Redux.

Я создал конечную точку регистрации для вставки зарегистрированных пользователей в мою базу данных MySQL.

Затем я создал конечную точку входа для выбора зарегистрированных пользователей из моей базы данных MySQL.

Как выглядит моя структура папок

SignUpAndSignInComponent просто возвращает мои компоненты регистрации и входа.

Мой компонент регистрации использует перехваты для установки локального состояния, которое затем передается на мой серверный сервер через мою конечную точку регистрации.

Мой компонент входа в систему также использует перехваты для установки локального состояния, которое затем передается моему бэкэнду для извлечения пользователя из базы данных.

На данный момент я отобразил только, какой пользователь вошел в систему, но только на странице SignUpAndSignIn.

Я хочу иметь возможность отображать пользователя, вошедшего в систему, в моем заголовке, поэтому мне нужно поднять состояние.

Что я сделал сейчас, так это ввел Redux для управления своим состоянием и сохранения состояния пользователя в одном большом хранилище, чтобы все компоненты могли иметь к нему доступ.

Мой компонент входа отправил действие в хранилище с помощью useDispatch

Я хочу иметь возможность принимать имя пользователя от вошедшего в систему пользователя и передавать его в мой компонент заголовка на самом верхнем уровне приложения.

Мой самый верхний компонент приложения использует useSelector для извлечения пользователя, вошедшего в систему, из хранилища и отображает его в моем заголовке.

Мне удалось сделать это, создав свой магазин, rootReducer, user reducer, action и types . Я даже сохранил состояние, чтобы оно было видно на уровне приложения, используя redux-persist и локальное хранилище.

Моя проблема в том, что когда пользователь входит в систему (нажимает кнопку отправки в форме входа), он обновляет только локальное состояние и отображает состояние в своем локальном представлении.

Только когда я вхожу во второй раз, мое действие отправляется в хранилище, чтобы я мог просмотреть вошедшего в систему пользователя на верхнем уровне. Почему это?

Нужно ли мне использовать Redux-thunk для обработки этих асинхронных запросов? Это то, что я думаю, что я должен сделать, но мне нужно больше ясности в том, в чем заключается реальная проблема.

вот что мое промежуточное программное обеспечение redux logger публикует в консоли

локальный просмотр, т.Е. Просмотр в компоненте входа

глобальный вид, т.Е. Просмотр внутри 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; ```
 

App.js с помощью useSelector

 ```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;
 
 

 

Ответ №1:

Похоже, это связано с тем, как вы выполняете асинхронный вызов в функции входа. Вы использовали async await, но вы также используете .then() Ваша функция CallBoth ожидает завершения функции входа в систему, но она не ожидает .затем вызывается для завершения, поэтому isUserLoggedIn отправляет состояние до его обновления — оно перехватывается при втором входе в систему, поэтому оно работает после второй попытки входа.

Вы могли бы просто удалить функцию CallBoth и поместить вызов isUserLoggedIn внутрь .затем после вызова setState. Тогда функция входа в систему будет вашим обработчиком отправки

ОТРЕДАКТИРОВАНО

При этом вам не понадобится, чтобы функция входа была асинхронной, ожидающей. Кроме того, вам на самом деле не нужно будет устанавливать данные ответа в состояние — вы можете передать их прямо в isUserLoggedIn. Поскольку вы храните данные в хранилище redux, вам вообще не нужно хранить их в состоянии компонента. Просто используйте свой селектор, чтобы получить его из redux

Вы можете изменить свою функцию isUserLoggedIn, чтобы принимать данные ответа в качестве параметра, подобного этому:

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

затем в вашей функции входа в систему:

 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);
         }

      });
   }
 

Комментарии:

1. Хорошо, я изменил его на imgur.com/ZVhdvsD но все равно придется дважды подавать заявку на обновление состояния.

2. Вот мой вывод на консоль с помощью redux logger: imgur.com/BP6ddZK

3. Я только что добавил к своему ответу — вызовы setState являются асинхронными, поэтому он по-прежнему не получает правильные данные из состояния — как и в моем обновленном ответе, вы можете передать данные ответа в качестве параметра в isUserLoggedIn

4. Хорошо, поэтому я удалил async await. Я не уверен, что вы имеете в виду, передавая данные ответа в isUserLoggedIn, это только для отправки действия. Таким образом, функция входа в систему просто устанавливает состояние для вошедшего в систему пользователя. Как только статус входа имеет имя пользователя, он отправляется как действие в хранилище. Я не могу отправить, пока не установлю LoginStatus с именем пользователя.

5. Я привел пример в конце своего ответа — надеюсь, это поможет