[英]setState not updating object state in functional component
我对本机反应有点陌生,我遇到了一个有趣的情况。 我有一个登录屏幕,其中有两个字段;
在登录按钮按下我正在执行两个功能:
我还有一个名为“errors”的全局 state,最初它是空对象({})和“setErrors”function 来更新它。 完整代码如下所示
const [errors, setErrors] = useState({});
const handleLogin = () => {
handleMobileInput(mobileNo);
handlePasswordInput(password);
if (errors.mobError || errors.passError) {
return;
}
}
const handleMobileInput = (mobileNo) => {
if (!mobileNo) {
const x = { ...errors, mobError: 'mobile no. is required'}
console.log(x); //outputs correctly
setErrors(x);
console.log(errors); //output incorrectly even when I click login again and again
} else if (mobileNo.length !== 10) {
setErrors({ ...errors, mobError: 'mobile no must be of 10 digits'});
} else {
setErrors({ ...errors, mobError: ''});
}
setMobileNo(mobileNo);
}
const handlePasswordInput = (password) => {
if (!password) {
setErrors({ ...errors, passError: 'password is required'})
} else if (password.length < 5) {
setErrors({ ...errors, passError: 'password must be 6 characters long'});
} else {
setErrors({ ...errors, passError: ''})
}
setPassword(password);
}
现在当我点击登录时,handleLogin() function 执行,它首先验证手机号码,因为手机号码是空的(因为我没有接触它,我直接按登录),handleMobileInput() 中的变量 x 更新为{ mobError: 'mobile no is required }
,这是正确的。 但是当我尝试将变量 x 设置为我的 state 时,它不会更新它并返回空的 object。 但它确实正确更新了密码 state。 现在我最终的 state '错误'看起来像这样
{
passError: 'password is required'
}
但我需要它
{
mobError: 'mobile no is required',
passError: 'pass is required'
}
不管我按了多少次登录按钮,这个 state 保持不变,而 mobError 的 state 永远不会更新。 所有这些功能都在我的主要功能组件中。 完整代码如下所示
import React from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity, CheckBox } from 'react-native';
import { Button, Card, Title, Divider } from 'react-native-paper';
import { AntDesign } from '@expo/vector-icons';
import { default as globalStyles } from '../globalStyles';
import { useState } from 'react';
const LoginScreen = ({ navigation }) => {
const [errors, setErrors] = useState({});
const [mobileNo, setMobileNo] = useState('');
const [password, setPassword] = useState('');
const handleMobileInput = (mobileNo) => {
if (!mobileNo) {
const x = { ...errors, mobError: 'mobile no. is required'}
console.log(x); //outputs correctly
setErrors(x);
console.log(errors); //output incorrectly even when I click login again and again
} else if (mobileNo.length !== 10) {
setErrors({ ...errors, mobError: 'mobile no must be of 10 digits'});
} else {
setErrors({ ...errors, mobError: ''});
}
setMobileNo(mobileNo);
}
const handlePasswordInput = (password) => {
if (!password) {
setErrors({ ...errors, passError: 'password is required'})
} else if (password.length < 5) {
setErrors({ ...errors, passError: 'password must be 6 characters long'});
} else {
setErrors({ ...errors, passError: ''})
}
setPassword(password);
}
const handleLogin = () => {
handleMobileInput(mobileNo);
handlePasswordInput(password);
if (errors.mobError || errors.passError) {
return;
}
}
return (
<View style={globalStyles.viewStyle}>
<Card style={globalStyles.cardStyle}>
<Card.Content>
<Title style={globalStyles.title}>Welcome to Mshur</Title>
<Divider style={{...globalStyles.divider, ...globalStyles.bgColorPrimary}}></Divider>
</Card.Content>
<View style={globalStyles.inputView}>
<AntDesign name="user" size={24} color="white" style={{...globalStyles.inputIconStyle, ...globalStyles.bgColorPrimary}}/>
<TextInput
style={globalStyles.inputStyle}
placeholder="enter mobile number"
placeholderTextColor="grey"
keyboardType="numeric"
onChange = {(e) => {
handleMobileInput(e.nativeEvent.text);
}}>
</TextInput>
</View>
{
errors.mobError ?
(<Text style={globalStyles.error}>{errors.mobError}</Text>)
: null
}
<View style={{...globalStyles.inputView, ...globalStyles.marginTop_1}}>
<AntDesign name="lock" size={24} color="white" style={{...globalStyles.inputIconStyle, ...globalStyles.bgColorPrimary}}/>
<TextInput
style={globalStyles.inputStyle}
placeholder="enter password"
placeholderTextColor="grey"
onChange = {(e) => {
handlePasswordInput(e.nativeEvent.text);
}}
>
</TextInput>
</View>
{
errors.passError ?
(<Text style={globalStyles.error}>{errors.passError}</Text>)
: null
}
<View style={styles.loginHelp}>
<View style={globalStyles.checkboxContainer}>
<CheckBox value={true}></CheckBox>
<Text style={{fontSize: 13}}>Remember me?</Text>
</View>
<TouchableOpacity style={globalStyles.TouchableOpacityStyle}>
<Text style={{color: 'grey'}}>Forgot Password</Text>
</TouchableOpacity>
</View>
<Button
style={{marginHorizontal: 15, ...globalStyles.bgColorPrimary, ...globalStyles.buttonStyle}}
mode="contained"
contentStyle = {{ height: 40 }}
onPress={() => handleLogin()}>
Login
</Button>
<Button
style={{marginHorizontal: 15, ...globalStyles.bgColorSeconday, ...globalStyles.marginTop_1, ...globalStyles.buttonStyle}}
mode="contained"
contentStyle = {{ height: 40 }}
onPress={() => navigation.navigate('SignUp')}>
New user? sign up
</Button>
</Card>
</View>
)
}
const styles = StyleSheet.create({
loginHelp: {
display:'flex',
flexDirection: 'row',
justifyContent: 'space-between',
marginHorizontal: 15,
},
});
export default LoginScreen;
setErrors
是异步的,不会立即显示更新的 state。
要检查更新errors
state,您应该将其记录在useEffect
中。
useEffect(()=>{
console.log(errors) // will execute everytime errors change
},[errors])
要了解更多信息,您可以查看此链接。
更新在您的情况下,您同时调用handleMobileInput
和handlePasswordInput
并且它们都有setErrors
,这将异步更新 state 并且您遇到的问题是由于竞争条件而发生的。
因此,当您编写setErrors({...errors, passError: 'password is required'})
时,此时errors
object 没有更新的mobError
属性,因此该属性始终丢失。
为了克服这个问题,我建议你不要在这两个函数中调用setErrors
而是从它们返回一个error
字符串。 然后在您的handleVote
方法中只调用一次setErrors
。 请检查下面的代码。
const handleMobileInput = (mobileNo) => {
let mobileError;
if (!mobileNo) {
mobileError = 'mobile no. is required'
} else if (mobileNo.length !== 10) {
mobileError = 'mobile no must be of 10 digits';
} else {
mobileError = ''
}
setMobileNo(mobileNo);
return mobileError;
}
const handlePasswordInput = (password) => {
let passwordError;
if (!password) {
passwordError = 'password is required'
} else if (password.length < 5) {
passwordError = 'password is required'
} else {
passwordError = 'password is required'
}
setPassword(password);
return passwordError;
}
const handleLogin = () => {
const mobError = handleMobileInput(mobileNo);
const passError = handlePasswordInput(password);
setErrors({...errors,passError,mobError})
if (mobError || passError) {
return;
}
}
useEffect(()=>{console.log(errors},[errors]) // will have updated fields
setState
以async
方式工作。 因此,您不会立即获得更新的值。
您可以为此使用useEffect
并将 state 作为依赖项传递,因此每次errors
state 得到更改时,它都会被调用。
要实现您的目标,请使用useEffect挂钩:
useEffect(() => console.log(errors), [errors])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.