React Native子组件更新父组件触发警告的原因及解决方法咨询
问题根本原因
你遇到的警告是因为EmailInput组件在渲染过程中执行了副作用操作:你在控制HelperText显示的isEmailValid()方法里直接调用了父组件传的notifyIfEmailIsValid去更新父组件状态,而React不允许在组件渲染阶段更新其他组件的状态,因此抛出了警告。
解决方案
拆分逻辑即可解决问题,不需要把校验逻辑迁移到父组件,完全不影响原有组件的复用性:把「校验输入合法性给当前组件用来显示错误」和「通知父组件校验结果」这两个逻辑分开,通知父组件的操作放到useEffect里执行,useEffect会在组件渲染完成后才触发,不会违反React的渲染规则。
修改后的EmailInput组件代码
import { useEffect } from 'react'; const EmailInput = props => { const isValidEmail = email => // eslint-disable-next-line no-useless-escape /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test( email, ); // 纯计算函数:仅返回校验结果,无任何副作用,渲染时调用不会触发问题 const getEmailValidState = () => { if (props.value != '') { return isValidEmail(props.value); } else { // 空值时当前组件不显示错误 return true; } }; // 输入值变化时,渲染完成后再通知父组件校验结果 useEffect(() => { if (props.value != '') { const isValid = isValidEmail(props.value); props.notifyIfEmailIsValid(isValid); } else { props.notifyIfEmailIsValid(false); } }, [props.value]); // 仅当输入值变化时才触发校验通知 return ( <View style={styles.inputFieldStyles}> <TextInput style={{ ...styles.textInput, ...props.style, width: props.width || Dimensions.DIMENSION_100_PERCENT, backgroundColor: Colors.COLOR_TRANSPARENT, color: props.color || Colors.COLOR_WHITE, borderColor: Colors.COLOR_WHITE, }} theme={{ colors: { placeholder: 'white', text: 'white', primary: 'white', underlineColor: 'transparent', background: '#003489', }, }} keyboardType="email-address" value={props.value} onChange={props.onChange} onChangeText={props.onChangeText} label={props.placeholder} underlineColor={Colors.COLOR_WHITE} autoCapitalize="none" returnKeyType={'done'} blurOnSubmit={true} /> <HelperText type="error" visible={!getEmailValidState()} style={styles.errorTextStyles}> {i18n.t('malformed_email')} </HelperText> </View> ); }; export default EmailInput;
PasswordInput组件的修改逻辑完全一致,把对应的校验规则、通知方法名替换成密码相关的即可。
可选优化建议
你当前父组件里同时维护了顶层的isEmailValid/isPasswordValid状态,和state对象里的emailIsValid/passwordIsValid,属于重复状态,可以合并到同一个状态里减少不必要的渲染。
内容的提问来源于stack exchange,提问作者Emanuel Dellsén
相关产品推荐
相关产品推荐

