React异步登录请求完成前阻止组件render调用的方案问询
Hey there, let's break down what's happening here and fix it step by step.
The core issue is that your handleLogin function makes an asynchronous axios call, but you're not waiting for it to complete before your component re-renders. Right now, when you call handleLogin(this.state) in handleSubmit, it fires off the POST request and immediately returns—so your component's render method runs right away, before localStorage gets updated, hence isLoggedIn() still returns false.
Here's how to fix this:
1. Make handleLogin return a Promise
First, update your handleLogin function in /utils/auth.js to return the axios promise chain. This lets you wait for the login request to finish in your component:
// in /utils/auth.js import axios from 'axios' const isBrowser = typeof window !== `undefined` const getUser = () => window.localStorage.gatsbyUser ? JSON.parse(window.localStorage.gatsbyUser) : {} const setUser = user => (window.localStorage.gatsbyUser = JSON.stringify(user)) export const handleLogin = ({ email, password }) => { if (!isBrowser) return Promise.resolve(false); return axios.post(`${process.env.API_URL}/sessions`, { email, password }) .then(response => { console.log(response); setUser({ name: `Jim`, legalName: `James K. User`, email: email, }); return true; // Indicate login success }) .catch(error => { console.error('Login failed:', error); return false; // Indicate login failure }); }; export const isLoggedIn = () => { if (!isBrowser) return false const user = getUser() return !!user.email }
2. Add a loading state to your Login component
Add a isLoggingIn state variable to track when the login request is in progress. This lets you block the form from re-rendering prematurely and show a loading state to the user:
import React from "react" import { Redirect } from "react-router-dom" import axios from 'axios' import Form from "./Form" import View from "./View" import { handleLogin, isLoggedIn } from "../utils/auth" export default class Login extends React.Component { constructor(props) { super(props); this.state = { email: ``, password: ``, isLoggingIn: false, // New loading state }; } handleUpdate(event) { this.setState({ [event.target.name]: event.target.value, }) } async handleSubmit(event) { event.preventDefault(); // Mark login as in progress this.setState({ isLoggingIn: true }); // Wait for the login request to complete const loginSuccessful = await handleLogin(this.state); // Mark login as finished this.setState({ isLoggingIn: false }); // Optional: Handle failure case (e.g., show error message) if (!loginSuccessful) { // Add code here to show an error to the user console.log('Login failed!'); } } render() { // Redirect if already logged in if (isLoggedIn()) { return <Redirect to={{ pathname: `/app/profile` }} /> } // Show loading state while login is in progress if (this.state.isLoggingIn) { return ( <View title="Log In"> <div>Logging in, please wait...</div> </View> ); } // Render the login form normally return ( <View title="Log In"> <Form handleUpdate={e => this.handleUpdate(e)} handleSubmit={e => this.handleSubmit(e)} /> </View> ) } }
Why this works:
- By making
handleLoginreturn a Promise, we can useasync/awaitinhandleSubmitto wait for the axios request to finish before updating the component state. - The
isLoggingInstate ensures that while the login request is pending, we show a loading state instead of the form, preventing any premature re-renders that would checkisLoggedIn()before localStorage is updated. - Once the request completes and
setUserupdates localStorage, the next re-render (triggered bysetState({ isLoggingIn: false })) will detect thatisLoggedIn()returns true and trigger the Redirect.
Bonus: Consider moving user state to component state (optional)
If you want to avoid relying on localStorage for immediate state checks, you could also track the user directly in your component's state (or use a global state manager like Context API or Redux). This would make state updates more synchronous and avoid any edge cases with localStorage. But the above solution is the most straightforward fix for your current setup.
内容的提问来源于stack exchange,提问作者Matt

