React Native两种组件渲染方式为何导致输入时键盘消失?
问题
我有一个包含输入框的简单组件,输入内容时仅目标输入框会变化,目前功能正常。组件代码如下:
const ScaleLabel = (): JSX.Element => { return ( <View> {scaleLabel.map((label, i) => ( <TextInput value={label} onChangeText={(text) => setScaleLabel( scaleLabel.map((item, index) => (index === i ? text : item)) ) } /> ))} </View> ); };
我原本通过以下方式渲染该组件:
{choice === "Escala de 0 a 10" && ScaleLabel()}
但当我改用以下方式渲染时:
{choice === "Escala de 0 a 10" && <ScaleLabel />}
每次输入内容时键盘都会消失,请问这是什么原因?
原因分析
组件实例的卸载与重建:
直接调用ScaleLabel()是在父组件的渲染流程中执行函数,返回的JSX会作为父组件DOM结构的一部分,不会生成独立的组件实例。而用<ScaleLabel />是声明式创建组件实例,当父组件的scaleLabel状态更新时,父组件会重新渲染,此时会销毁旧的ScaleLabel实例并创建新实例——旧实例被卸载时,输入框失去焦点,键盘自然消失。状态作用域的影响:
从代码能看出scaleLabel和setScaleLabel是父组件的状态。父组件状态更新触发重渲染时,<ScaleLabel />作为子组件会被重新初始化,而函数调用的方式只是重新执行函数生成JSX,不会触发组件的卸载/挂载流程,所以输入框焦点能保留。
解决方法
- 将状态移至组件内部:
如果业务逻辑允许,把scaleLabel状态放到ScaleLabel组件内部,这样父组件重渲染不会影响子组件的状态和实例,输入框焦点就能稳定保留:
const ScaleLabel = (): JSX.Element => { // 根据实际需求初始化状态 const [scaleLabel, setScaleLabel] = useState(["初始标签1", "初始标签2"]); return ( <View> {scaleLabel.map((label, i) => ( // 必须添加key,避免列表渲染的性能问题和警告 <TextInput key={i} value={label} onChangeText={(text) => setScaleLabel( scaleLabel.map((item, index) => (index === i ? text : item)) ) } /> ))} </View> ); };
- 用React.memo优化子组件:
如果状态必须留在父组件,用React.memo包裹ScaleLabel组件,减少不必要的重渲染:
const ScaleLabel = React.memo(() => { return ( <View> {scaleLabel.map((label, i) => ( <TextInput key={i} value={label} onChangeText={(text) => setScaleLabel( scaleLabel.map((item, index) => (index === i ? text : item)) ) } /> ))} </View> ); });
这样只有当组件的props(如果有传递)变化时,子组件才会重渲染,避免父组件状态更新时子组件被频繁重建。
内容的提问来源于stack exchange,提问作者José Carlos
相关产品推荐
相关产品推荐

