React Native自定义组件TextInput状态更新时键盘自动收起问题求助
解决React Native自定义组件内TextInput输入时键盘自动收起的问题
我看了你代码里的问题啦,核心原因是你把Mpin和Password这两个组件直接定义在了ChangePasswordScreen组件的内部。每次父组件里的状态(比如oldMpin、newMpin这些)更新触发重渲染时,这两个内部组件都会被重新创建一遍——相当于每次都是一个全新的组件实例,原来的TextInput自然就失去焦点,键盘也就跟着收起了。
下面给你两种可行的解决办法:
方法一:把内部组件提取为独立组件
把Mpin和Password从ChangePasswordScreen里抽出来,作为单独的组件,通过props传递需要的状态和回调函数:
// 单独定义Mpin组件 const Mpin = ({ oldMpin, setOldMpin, newMpin, setNewMpin, newnPinRef }) => { console.log("Old Mpin", oldMpin) return ( <Form style={{ marginBottom: 10 }} > <Item floatingLabel> <Label>Old Mpin</Label> <Input value={oldMpin} keyboardType={'numeric'} keyboardAppearance={'default'} maxLength={4} onChangeText={setOldMpin} onSubmitEditing={() => { newnPinRef.current._root.focus(); }} /> </Item> <Item floatingLabel> <Label>New Mpin</Label> <Input value={newMpin} keyboardType={'numeric'} keyboardAppearance={'default'} maxLength={4} onChangeText={setNewMpin} getRef={(input) => { newnPinRef.current = input; }} /> </Item> <Button bordered block success style={{ marginTop: '5%', marginLeft: '5%' }}> <Text>SUBMIT</Text> </Button> </Form> ) } // 单独定义Password组件 const Password = ({ oldPass, setOldPass, newPass, setNewPass, newPasswordRef, onSubmit }) => { return ( <Form> <Item floatingLabel> <Label>Old Password</Label> <Input value={oldPass} onChangeText={setOldPass} onSubmitEditing={() => { newPasswordRef.current._root.focus(); }} /> </Item> <Item floatingLabel> <Label>New Password</Label> <Input value={newPass} onChangeText={setNewPass} getRef={(input) => { newPasswordRef.current = input; }} /> </Item> <Button bordered block success style={{ marginTop: '5%', marginLeft: '5%' }} onPress={onSubmit} > <Text>SUBMIT</Text> </Button> </Form> ) } // 原ChangePasswordScreen组件 const ChangePasswordScreen = ({ navigation }) => { const [account, setAccount] = useState(''); const [visible, setVisible] = useState(false); const [pages, setPages] = useState(1); const [firstPageActive, setFirstPageActive] = useState(true); const [secondPageActive, setSecondPageActive] = useState(false); const [oldMpin, setOldMpin] = useState(null); const [newMpin, setNewMpin] = useState(null); const [oldPass, setOldPass] = useState(null); const [newPass, setNewPass] = useState(null); const [test, setTest] = useState(null); const [counter, setCounter] = useState(null); // 用useRef替代普通let,避免重渲染丢失引用 const oldPinRef = useRef(null); const newnPinRef = useRef(null); const oldPasswordRef = useRef(null); const newPasswordRef = useRef(null); const _firstpage = () => { setPages(1); setFirstPageActive(true); setSecondPageActive(false); } const _secondpage = () => { setPages(2); setFirstPageActive(false); setSecondPageActive(true); } //Logout const _onSubmit = async () => { let result = await AuthService.updateAccount( fullName, email, mobile, account.user_code ) if (result.status == '1') { await AuthService.setAccount(result.account); let acDetails = await AuthService.getAccount(); setAccount(acDetails) } else { alert("We are facing some issues") } } let shows = null; if (pages == 1) { shows = <Mpin oldMpin={oldMpin} setOldMpin={setOldMpin} newMpin={newMpin} setNewMpin={setNewMpin} newnPinRef={newnPinRef} /> } else if (pages == 2) { shows = <Password oldPass={oldPass} setOldPass={setOldPass} newPass={newPass} setNewPass={setNewPass} newPasswordRef={newPasswordRef} onSubmit={_onSubmit} /> } return ( <Container style={styles.container}> {visible ? <Loader visibility={visible} /> : <> <Header androidStatusBarColor="#00B386" style={{ backgroundColor: '#00B386' }} hasSegment > <Left> <Button transparent onPress={() => { navigation.goBack(null) }} > <Icon name='arrow-back' /> <Text>Back</Text> </Button> </Left> <Body> <Title>Password</Title> </Body> <Right> </Right> </Header> <Segment style={{ backgroundColor: '#00B386' }}> <Button first active={firstPageActive} style={[(pages == 1) ? styles.activeButton : styles.button, { width: '50%', height: '100%', borderColor: '#00B386', display: 'flex', justifyContent: 'center' }]} onPress={_firstpage} > <Text style={(pages == 1) ? styles.activeButtonText : styles.buttonText}>MPIN</Text> </Button> <Button active={secondPageActive} style={[(pages == 2) ? styles.activeButton : styles.button, { width: '50%', height: '100%', borderColor: '#00B386', display: 'flex', justifyContent: 'center' }]} onPress={_secondpage} > <Text style={(pages == 2) ? styles.activeButtonText : styles.buttonText}>Password</Text> </Button> </Segment> <Content padder> {shows} <Form> <Input value={test} onChangeText={(val) => { setCounter(counter + 1); setTest(val); }} /> </Form> </Content> </> } </Container> ); }
方法二:用useMemo缓存内部组件
如果不想把组件抽出来,也可以用useMemo来缓存Mpin和Password组件,确保只有当依赖的状态变化时才重新创建组件:
const ChangePasswordScreen = ({ navigation }) => { // ... 其他状态和逻辑保持不变 // 用useMemo缓存Mpin组件 const MpinComponent = useMemo(() => { return () => { console.log("Old Mpin", oldMpin) return ( <Form style={{ marginBottom: 10 }} > <Item floatingLabel> <Label>Old Mpin</Label> <Input value={oldMpin} keyboardType={'numeric'} keyboardAppearance={'default'} maxLength={4} onChangeText={(val)=>{setOldMpin(val)}} getRef={(input): void => { oldPin.current = input; }} onSubmitEditing={() => { newnPin.current._root.focus(); }} /> </Item> <Item floatingLabel> <Label>New Mpin</Label> <Input value={newMpin} keyboardType={'numeric'} keyboardAppearance={'default'} maxLength={4} onChangeText={(val) => { setNewMpin(val) }} getRef={(input): void => { newnPin.current = input; }} /> </Item> <Button bordered block success style={{ marginTop: '5%', marginLeft: '5%' }}> <Text>SUBMIT</Text> </Button> </Form> ) } }, [oldMpin, newMpin]); // 用useMemo缓存Password组件 const PasswordComponent = useMemo(() => { return () => { return ( <Form> <Item floatingLabel> <Label>Old Password</Label> <Input value={oldPass} onChangeText={(val) => { setOldPass(val) }} getRef={(input): void => { oldPassword.current = input; }} onSubmitEditing={() => { newPassword.current._root.focus(); }} /> </Item> <Item floatingLabel> <Label>New Password</Label> <Input value={newPass} onChangeText={(val) => { setNewPass(val) }} getRef={(input): void => { newPassword.current = input; }} /> </Item> <Button bordered block success style={{ marginTop: '5%', marginLeft: '5%' }} onPress={_onSubmit} > <Text>SUBMIT</Text> </Button> </Form> ) } }, [oldPass, newPass]); let shows = null; if (pages == 1) { shows = <MpinComponent /> } else if (pages == 2) { shows = <PasswordComponent /> } // ... 其余返回部分保持不变 }
另外还有个小细节要注意:你原来用let oldPin = useRef(null);这种写法不对,useRef返回的是一个对象,应该用const oldPin = useRef(null);,然后通过oldPin.current来访问引用的值,不然重渲染后引用会丢失哦。
内容的提问来源于stack exchange,提问作者Biswa Nath
相关产品推荐
相关产品推荐

