我无法在 React 中设置状态
I can't set a state from fetch in React
我正在尝试登录并设置当前用户。问题是登录成功,数据正确但是我无法设置状态,用户为空
UserContext.js
import React, { useContext, useState } from 'react';
const UserContext = React.createContext();
export const useAuth = () => {
return useContext(UserContext);
}
export const UserContextProvider = ( {children} ) => {
const [ user, setUser ] = useState({
name: '',
lastname: '',
username: '',
password: ''
});
const [ validation, setValidation ] = useState({
username: '',
password: ''
});
const setUserData = (e) => {
return ( {target: {value}} ) => {
setUser(data => ( {...data, [e]: value} ));
}
}
const setUserValidation = (e) => {
return ( {target: {value}} ) => {
setValidation(data => ( {...data, [e]: value} ));
}
}
const signUp = async () => {
return await fetch('http://localhost:8080/users/signup', {
method: 'POST',
body: JSON.stringify(user),
headers: { 'Content-Type': 'application/json' }
});
}
const signIn = async () => {
return await fetch('http://localhost:8080/users/signin', {
method: 'POST',
body: JSON.stringify(validation),
headers: { 'Content-Type': 'application/json' }
}).then((res) => res.json())
.then(data => {
console.log(data);
setUser({
name: data.name,
lastname: data.lastname,
username: data.username,
password: data.password
});
console.log(user);
});
}
const signOut = async () => {
await fetch('http://localhost:8080/users/signout');
setUser(null);
return;
}
return (
<UserContext.Provider value={{
user,
setUserData,
setUserValidation,
signUp,
signIn,
signOut
}}>
{ children }
</UserContext.Provider>
);
}
SignIn.js
import './SignIn.css';
import { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../../context/UserContext';
const SignIn = () => {
const { signIn, setUserValidation, user } = useAuth();
const [ errorMessage, setErrorMessage ] = useState(null);
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
setErrorMessage(null);
signIn().then(() => {
console.log(user);
setErrorMessage(null);
navigate('/');
}).catch(err => {
setErrorMessage('Error singing in, please try again.', err);
});
}
return (
<div className='SignIn'>
<h3>Sign In</h3>
<form className='LoginForm' onSubmit={handleSubmit}>
{ errorMessage && <h4 className='ErrorMessage'>{errorMessage}</h4> }
<input type='email' name='username' placeholder='Email' onChange={setUserValidation('username')} required/>
<input type='password' name='password' placeholder='Password' onChange={setUserValidation('password')} required/>
<button className='Login' type='submit'>Sign In</button>
</form>
<h5>Don't have an account?</h5><Link className='Redirect' to='/signup'>Sign Up</Link>
</div>
);
}
export default SignIn;
As you can see, the first console.log shows the correct user information, but then is empty after the setUser() and in the SignIn.js component.
我猜这里可能发生了什么。我没有时间验证我的猜测,所以如果我说错了请原谅我。
当您更改上下文中的状态时,将重新呈现提供者及其子项。在本例中,UserContextProvider 及其子项。
所以首先,请确保 SignIn 在 UserContextProvider 中呈现,例如将所有应用程序嵌入一个 .我通常在 index.js
文件中这样做。
ReactDOM.render(
<UserContextProvider>
{/* ... app here, that includes SignIn ... */}
</UserContextProvider>,
document.getElementById('root')
);
第二件事,因为你包含 console.log() 以便在你更改状态的同一渲染中执行,很明显它们不会反映 仅在后续渲染中可用。
我建议你把 console.log(user)
放在 SignIn 组件的开头,在 useNavigate()
之后说, 在 之外 handleSubmit
功能。
const SignIn = () => {
const { signIn, setUserValidation, user } = useAuth();
const [ errorMessage, setErrorMessage ] = useState(null);
const navigate = useNavigate();
console.log(user)
// ...ecc...
}
如果我是对的,这个 console.log 将被执行(至少)两次,一次用于初始渲染,一次用于由 setUser
触发的后续渲染(您还可以包括一个console.log 在 handleSubmit 中只是为了检测由 setUser
触发的重新渲染。在最后的渲染中,您应该看到用户数据。
如果这按我预期的那样工作,我想你可以用这样的方式处理登录
const SignIn = () => {
const { signIn, setUserValidation, user } = useAuth();
const [ errorMessage, setErrorMessage ] = useState(null);
const navigate = useNavigate();
// in the first rendering, the userName will be '', so it won't navigate
// if the component is re-rendered after the setUser in signIn,
// in this rendering there will be a userName, hence the navigation will proceed
if (user.userName !== '') {
navigate('/');
}
const handleSubmit = async (e) => {
e.preventDefault();
setErrorMessage(null);
signIn().catch(err => {
setErrorMessage('Error singing in, please try again.', err);
});
}
return (
// ... as before ...
);
}
编码愉快! - 卡洛斯
这是正常现象。您不能在设置状态后直接访问状态。更新后的状态将仅在下一次渲染时可用。上下文也是如此。
如果您需要在 SignIn()
之后直接访问用户数据,您可以这样做。
UserContext.js
const signIn = async () => {
return await fetch('http://localhost:8080/users/signin', {
method: 'POST',
body: JSON.stringify(validation),
headers: { 'Content-Type': 'application/json' }
}).then((res) => res.json())
.then(data => {
console.log(data);
const usr = {
name: data.name,
lastname: data.lastname,
username: data.username,
password: data.password
}
setUser(usr);
// console.log(user); <-- You can't do this
return usr // <--
});
}
SignIn.js
const handleSubmit = async (e) => {
e.preventDefault();
setErrorMessage(null);
signIn().then((usr) => { // <---
console.log(usr);
// console.log(user); <-- You can't do this
setErrorMessage(null);
navigate('/');
}).catch(err => {
setErrorMessage('Error singing in, please try again.', err);
});
}
我正在尝试登录并设置当前用户。问题是登录成功,数据正确但是我无法设置状态,用户为空
UserContext.js
import React, { useContext, useState } from 'react';
const UserContext = React.createContext();
export const useAuth = () => {
return useContext(UserContext);
}
export const UserContextProvider = ( {children} ) => {
const [ user, setUser ] = useState({
name: '',
lastname: '',
username: '',
password: ''
});
const [ validation, setValidation ] = useState({
username: '',
password: ''
});
const setUserData = (e) => {
return ( {target: {value}} ) => {
setUser(data => ( {...data, [e]: value} ));
}
}
const setUserValidation = (e) => {
return ( {target: {value}} ) => {
setValidation(data => ( {...data, [e]: value} ));
}
}
const signUp = async () => {
return await fetch('http://localhost:8080/users/signup', {
method: 'POST',
body: JSON.stringify(user),
headers: { 'Content-Type': 'application/json' }
});
}
const signIn = async () => {
return await fetch('http://localhost:8080/users/signin', {
method: 'POST',
body: JSON.stringify(validation),
headers: { 'Content-Type': 'application/json' }
}).then((res) => res.json())
.then(data => {
console.log(data);
setUser({
name: data.name,
lastname: data.lastname,
username: data.username,
password: data.password
});
console.log(user);
});
}
const signOut = async () => {
await fetch('http://localhost:8080/users/signout');
setUser(null);
return;
}
return (
<UserContext.Provider value={{
user,
setUserData,
setUserValidation,
signUp,
signIn,
signOut
}}>
{ children }
</UserContext.Provider>
);
}
SignIn.js
import './SignIn.css';
import { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../../context/UserContext';
const SignIn = () => {
const { signIn, setUserValidation, user } = useAuth();
const [ errorMessage, setErrorMessage ] = useState(null);
const navigate = useNavigate();
const handleSubmit = async (e) => {
e.preventDefault();
setErrorMessage(null);
signIn().then(() => {
console.log(user);
setErrorMessage(null);
navigate('/');
}).catch(err => {
setErrorMessage('Error singing in, please try again.', err);
});
}
return (
<div className='SignIn'>
<h3>Sign In</h3>
<form className='LoginForm' onSubmit={handleSubmit}>
{ errorMessage && <h4 className='ErrorMessage'>{errorMessage}</h4> }
<input type='email' name='username' placeholder='Email' onChange={setUserValidation('username')} required/>
<input type='password' name='password' placeholder='Password' onChange={setUserValidation('password')} required/>
<button className='Login' type='submit'>Sign In</button>
</form>
<h5>Don't have an account?</h5><Link className='Redirect' to='/signup'>Sign Up</Link>
</div>
);
}
export default SignIn;
As you can see, the first console.log shows the correct user information, but then is empty after the setUser() and in the SignIn.js component.
我猜这里可能发生了什么。我没有时间验证我的猜测,所以如果我说错了请原谅我。
当您更改上下文中的状态时,将重新呈现提供者及其子项。在本例中,UserContextProvider 及其子项。
所以首先,请确保 SignIn 在 UserContextProvider 中呈现,例如将所有应用程序嵌入一个 .我通常在 index.js
文件中这样做。
ReactDOM.render(
<UserContextProvider>
{/* ... app here, that includes SignIn ... */}
</UserContextProvider>,
document.getElementById('root')
);
第二件事,因为你包含 console.log() 以便在你更改状态的同一渲染中执行,很明显它们不会反映 仅在后续渲染中可用。
我建议你把 console.log(user)
放在 SignIn 组件的开头,在 useNavigate()
之后说, 在 之外 handleSubmit
功能。
const SignIn = () => {
const { signIn, setUserValidation, user } = useAuth();
const [ errorMessage, setErrorMessage ] = useState(null);
const navigate = useNavigate();
console.log(user)
// ...ecc...
}
如果我是对的,这个 console.log 将被执行(至少)两次,一次用于初始渲染,一次用于由 setUser
触发的后续渲染(您还可以包括一个console.log 在 handleSubmit 中只是为了检测由 setUser
触发的重新渲染。在最后的渲染中,您应该看到用户数据。
如果这按我预期的那样工作,我想你可以用这样的方式处理登录
const SignIn = () => {
const { signIn, setUserValidation, user } = useAuth();
const [ errorMessage, setErrorMessage ] = useState(null);
const navigate = useNavigate();
// in the first rendering, the userName will be '', so it won't navigate
// if the component is re-rendered after the setUser in signIn,
// in this rendering there will be a userName, hence the navigation will proceed
if (user.userName !== '') {
navigate('/');
}
const handleSubmit = async (e) => {
e.preventDefault();
setErrorMessage(null);
signIn().catch(err => {
setErrorMessage('Error singing in, please try again.', err);
});
}
return (
// ... as before ...
);
}
编码愉快! - 卡洛斯
这是正常现象。您不能在设置状态后直接访问状态。更新后的状态将仅在下一次渲染时可用。上下文也是如此。
如果您需要在 SignIn()
之后直接访问用户数据,您可以这样做。
UserContext.js
const signIn = async () => {
return await fetch('http://localhost:8080/users/signin', {
method: 'POST',
body: JSON.stringify(validation),
headers: { 'Content-Type': 'application/json' }
}).then((res) => res.json())
.then(data => {
console.log(data);
const usr = {
name: data.name,
lastname: data.lastname,
username: data.username,
password: data.password
}
setUser(usr);
// console.log(user); <-- You can't do this
return usr // <--
});
}
SignIn.js
const handleSubmit = async (e) => {
e.preventDefault();
setErrorMessage(null);
signIn().then((usr) => { // <---
console.log(usr);
// console.log(user); <-- You can't do this
setErrorMessage(null);
navigate('/');
}).catch(err => {
setErrorMessage('Error singing in, please try again.', err);
});
}