React Native中仅传递计算布局+键盘高度给样式为何引发无限循环?
问题分析与解决方案
你遇到的无限循环是布局变化触发状态更新,状态更新又反向触发布局变化形成的循环链:
- 当Autocomplete展开长列表时,包裹它的View因为设置了固定
height,会引发绑定onLayout的父View布局重新计算,导致layout.height变动 layout.height变化触发useEffect执行,更新height状态height状态更新又导致包裹Autocomplete的View高度变化,再次触发父View的onLayout,循环往复
具体修复方案
方案1:直接在渲染时计算高度,避免状态更新触发布局
去掉useState和useEffect,直接在style中实时计算高度,减少状态更新带来的布局连锁反应:
import { useLayout } from '@react-native-community/hooks' const App = () => { const { onLayout, ...layout } = useLayout() const keyboardHeight = useKeyboard(); //custom hook to get keyboard hight return ( <View onLayout={onLayout}> <SearchBar /> <View style={{height: layout.height - keyboardHeight || 0}}> <AutoComplete/> </View> </View> ) }
方案2:用Ref保存父容器初始高度,避免依赖动态变化的布局值
如果父容器高度在组件挂载后不会因非键盘因素改变,用Ref保存初始高度,仅监听键盘高度变化更新子容器高度:
import { useLayout } from '@react-native-community/hooks' import { useEffect, useState, useRef } from 'react' const App = () => { const { onLayout, ...layout } = useLayout() const keyboardHeight = useKeyboard(); const containerHeightRef = useRef(0); const [height, setHeight] = useState(0); // 仅在组件挂载时保存父容器初始高度 useEffect(() => { if (layout.height > 0) { containerHeightRef.current = layout.height; setHeight(layout.height - keyboardHeight); } }, []); // 仅监听键盘高度变化更新子容器高度 useEffect(() => { setHeight(containerHeightRef.current - keyboardHeight); }, [keyboardHeight]) return ( <View onLayout={onLayout}> <SearchBar /> <View style={{height: height}}> <AutoComplete/> </View> </View> ) }
额外优化点
- 检查
useKeyboard自定义hook是否会在键盘动画过程中连续触发更新,若存在这种情况,建议在键盘动画结束后再更新高度,避免频繁触发状态变化 - 给Autocomplete组件设置
maxHeight属性,让列表在固定高度内滚动,从根源减少布局变动的可能
内容的提问来源于stack exchange,提问作者learncode
相关产品推荐
相关产品推荐

