我对 React Native 有点陌生,我遇到了一个有趣的情况。 我有一个登录屏幕,其中有两个字段;
在登录按钮按下我正在执行两个功能:
我还有一个名为“errors”的全局状态,最初它是空对象({})和“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);
}
现在,当我点击 login 时,handleLogin() 函数会执行并首先验证手机号码
因为手机号是空的(因为我没碰过,直接按登录),handleMobileInput()里面的变量x更新为{ 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;
答案 0 :(得分:0)
setErrors
是异步的,不会立即显示更新的状态。
要检查更新 errors
状态,您应该将其记录在 useEffect
中。
useEffect(()=>{
console.log(errors) // will execute everytime errors change
},[errors])
要了解更多信息,您可以查看此link.
更新
在您的情况下,您同时调用 handleMobileInput
和 handlePasswordInput
并且它们都有 setErrors
它将异步更新状态,并且由于竞争条件而导致您遇到的问题.
因此,当您编写 setErrors({ ...errors, passError: 'password is required'})
时,此时 errors
对象没有更新的 mobError
属性,因此该属性始终丢失。
为了克服这个问题,我建议您不要在这两个函数中都调用 setErrors
,而是从它们返回一个 error
字符串。然后在您的 setErrors
方法中只调用一次 handleVote
。
请检查下面的代码。
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
答案 1 :(得分:0)
setState
以 async
的方式工作。因此,您不会立即获得更新的值。
您可以为此使用 useEffect
并将状态作为依赖项传递,因此每次 errors
状态发生更改时,它都会被调用。
要实现您的目标,请使用 useEffect 钩子:
useEffect(() => console.log(errors), [errors])