You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 23:17:31