React Native中TextInput输入字符时失焦的问题排查与解决
问题:React Native TextInput输入字符后失去焦点
我按照React Native文档编写了一个绑定状态的TextInput组件,代码如下:
function NewItemView(): JSX.Element { const [text, setText] = useState(""); const dispatch = useDispatch(); const submitText = () => { dispatch(createRecord(text)) setText("") } return ( <View> <TextInput style={{ backgroundColor: '#eeeeff', borderBottomColor: '#000000', borderBottomWidth: 1, }} onChangeText={setText} value={text} /> <Button title='Create new record' onPress={submitText} /> </View> ) }
在iPhone模拟器中,每当我在文本输入框中输入一个字符,输入框就会失去焦点,必须重新点击才能继续输入。该视图的使用方式如下:
function TodoList(): JSX.Element { const state: State = useSelector((state: any) => state.list); const dispatch = useDispatch(); useEffect(() => { dispatch(fetchList) }, []) return ( <View> <NewItemView/> <FlatList data={state.data} renderItem={({item}) => <ItemCell text={item.text} />} /> </View> ) }
原因分析
最可能的问题是**NewItemView被嵌套定义在TodoList组件内部**(你贴的代码里是独立的,但实际代码可能存在嵌套)。每次TodoList重新渲染时,NewItemView都会被重新定义为一个全新的组件类型,React会卸载旧的组件实例并挂载新的,直接导致TextInput失去焦点。
另外,如果useSelector的选择器返回的状态每次都是新对象,会导致TodoList频繁重新渲染,虽然不会直接触发卸载,但也可能间接引发焦点丢失问题。
解决方案
- 将
NewItemView移到TodoList外部独立定义
这是最直接的解决方式,确保组件不会随父组件渲染被重复创建:
// 独立定义NewItemView,放在TodoList外部 function NewItemView(): JSX.Element { const [text, setText] = useState(""); const dispatch = useDispatch(); const submitText = () => { dispatch(createRecord(text)) setText("") } return ( <View> <TextInput style={{ backgroundColor: '#eeeeff', borderBottomColor: '#000000', borderBottomWidth: 1, }} onChangeText={setText} value={text} /> <Button title='Create new record' onPress={submitText} /> </View> ) } function TodoList(): JSX.Element { const state: State = useSelector((state: any) => state.list); const dispatch = useDispatch(); useEffect(() => { dispatch(fetchList) }, []) return ( <View> <NewItemView/> <FlatList data={state.data} renderItem={({item}) => <ItemCell text={item.text} />} keyExtractor={(item) => item.id} // 建议添加,优化FlatList性能 /> </View> ) }
- 若必须嵌套定义,用
React.memo记忆组件
如果因为某些原因必须把NewItemView放在TodoList内部,使用React.memo包裹组件,避免不必要的重新渲染和卸载:
function TodoList(): JSX.Element { const state: State = useSelector((state: any) => state.list); const dispatch = useDispatch(); useEffect(() => { dispatch(fetchList) }, []) // 用React.memo包裹,记忆组件 const NewItemView = React.memo(() => { const [text, setText] = useState(""); const dispatch = useDispatch(); const submitText = () => { dispatch(createRecord(text)) setText("") } return ( <View> <TextInput style={{ backgroundColor: '#eeeeff', borderBottomColor: '#000000', borderBottomWidth: 1, }} onChangeText={setText} value={text} /> <Button title='Create new record' onPress={submitText} /> </View> ) }) return ( <View> <NewItemView/> <FlatList data={state.data} renderItem={({item}) => <ItemCell text={item.text} />} keyExtractor={(item) => item.id} /> </View> ) }
另外,建议给FlatList添加keyExtractor,使用数据项的唯一标识作为key,避免使用默认的index引发性能和渲染异常问题。
内容的提问来源于stack exchange,提问作者sak
相关产品推荐
相关产品推荐

