React Native中useCallback获取state始终返回初始值的问题
问题原因及解决方法
原因
- 闭包捕获旧状态:
useCallback依赖数组为空时,renderItem函数会被缓存,内部始终引用组件初始渲染时的todos(空数组),所以console.log(todos.length)始终输出0。 - 列表组件复用导致旧闭包残留:即便添加
todos或todos.length到依赖数组,FlashList的列表项复用机制会让已渲染的项保留旧的onPress闭包,依旧读取旧状态值。
解决方法
方法一:利用函数式更新获取最新状态
既然setTodos已经使用函数式更新获取当前状态,直接把长度判断逻辑移到setTodos的回调里,无需依赖外部todos,同时简化useCallback的依赖:
export default function MyComponent() { const [todos, setTodos] = useState<string[]>([]); const renderItem = useCallback( ({ item }: ListRenderItemInfo<Todo>) => ( <TouchableOpacity style={{ height: 50 }} onPress={() => { setTodos((curr) => { console.log(curr.length); // 获取最新状态长度 if (curr.length >= 10) return curr; return [item.name, ...curr]; }); }} > <Heading color="black">{item.name}</Heading> </TouchableOpacity> ), [] // 无需依赖todos ); return ( <FlashList<Todo> data={data?.todos as Todo[]} estimatedItemSize={50} renderItem={renderItem} keyExtractor={(_, idx) => idx.toString()} /> ); }
方法二:用useRef保存最新状态
通过useRef实时同步todos的最新值,突破闭包限制,直接读取最新状态:
export default function MyComponent() { const [todos, setTodos] = useState<string[]>([]); const todosRef = useRef<string[]>([]); // 状态更新时同步到ref useEffect(() => { todosRef.current = todos; }, [todos]); const renderItem = useCallback( ({ item }: ListRenderItemInfo<Todo>) => ( <TouchableOpacity style={{ height: 50 }} onPress={() => { const currentLength = todosRef.current.length; console.log(currentLength); // 获取最新长度 if (currentLength >= 10) return; setTodos((curr) => [item.name, ...curr]); }} > <Heading color="black">{item.name}</Heading> </TouchableOpacity> ), [] // 无需依赖todos ); return ( <FlashList<Todo> data={data?.todos as Todo[]} estimatedItemSize={50} renderItem={renderItem} keyExtractor={(_, idx) => idx.toString()} /> ); }
内容的提问来源于stack exchange,提问作者Californium
相关产品推荐
相关产品推荐

