#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. Я привел пример в конце своего ответа — надеюсь, это поможет