setState 不更新功能组件中的对象状态



我对 react native 有点陌生,我遇到了一个有趣的情况。 我有一个登录屏幕,其中有两个字段;

  1. 手机号码。
  2. 密码

在登录按钮上,按我正在执行两个功能:

  1. handleMobileInput() - 检查手机号码是否有效。 2 handlePasswordInput() - 这将检查密码是否有效。

我还有一个名为"错误"的全局状态,最初它是空对象({})和"setErrors"函数来更新它。完整代码如下所示

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() 函数执行并首先验证手机号码 由于手机号码是空的(因为我没有碰过它,所以我直接按登录),手柄中的变量 x MobileInput() 更新为{ mobError: 'mobile no is required }这是正确的。但是当我尝试将变量 x 设置为我的状态时,它不会更新它并返回空对象。但它确实会正确更新密码状态。现在我的最终状态"错误"如下所示

{
passError: 'password is required'
}

但我需要它作为

{
mobError: 'mobile no is required',
passError: 'pass is required'
}

无论我按下登录按钮多少次,此状态都保持不变,并且 mobError 的状态永远不会更新。 所有这些功能都在我的主要功能组件中。 完整代码如下所示

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是异步的,不会立即显示更新的状态。

要检查更新errors状态,您应该将其记录在useEffect中。

useEffect(()=>{
console.log(errors) // will execute everytime errors change
},[errors])

要了解更多信息,您可以查看此链接。

更新在您的情况下,您同时调用handleMobileInputhandlePasswordInput,并且它们都有setErrors,这将异步更新状态,并且由于竞争条件而发生您遇到的问题。

所以当你写setErrors({ ...errors, passError: 'password is required'})时,此时errors对象没有更新的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

setStateasync工作方式。因此,您不会立即获得更新的值。

您可以使用useEffect并将状态作为依赖项传递,因此每次状态发生更改时errors都会被调用。

要实现您的目标,请使用 useEffect 钩子:

useEffect(() => console.log(errors), [errors])

最新更新