为何在Deno Fresh中用对象/数组作为useState参数会触发无限循环?
无限循环的原因及解决办法
原因分析
组件每次渲染都会执行函数体内的全部代码,问题出在Preact对不同类型状态的更新判断逻辑上:
- 字符串(基本类型):基本类型的比较是值比较。第一次渲染时
state为'hi',调用setState('a')会触发重渲染;第二次渲染时state已经是'a',再次调用setState('a')时,新旧值完全相等,Preact判定状态无变化,不会触发重渲染,因此不会循环。 - 对象(引用类型):引用类型的比较是内存地址(引用)比较。每次组件渲染都会创建一个全新的
{a: 'a'}对象,即使内容相同,内存地址也不一样。调用setState(obj)时,Preact发现新对象和旧对象引用不同,就会判定状态更新,触发重渲染。重渲染后又会创建新对象、调用setState,如此反复形成无限循环。
解决办法
1. 把对象移到组件外部
让组件每次渲染都复用同一个对象引用:
import { useState } from "preact/hooks"; // 移到组件外部,只创建一次 const obj = {a: 'a'} export default function(){ const [state, setState] = useState('hi') setState(obj) console.log(state) }
2. 用useMemo缓存对象
通过useMemo固定对象引用,仅在依赖变化时重新创建:
import { useState, useMemo } from "preact/hooks"; export default function(){ const [state, setState] = useState('hi') // 空依赖数组,仅组件挂载时创建一次对象 const obj = useMemo(() => ({a: 'a'}), []) setState(obj) console.log(state) }
3. 避免在渲染顶层调用setState
这是最规范的做法:将setState放在事件处理函数或useEffect副作用中,只在需要时触发状态更新:
import { useState, useEffect } from "preact/hooks"; export default function(){ const [state, setState] = useState('hi') // 仅组件挂载时执行一次状态更新 useEffect(() => { setState({a: 'a'}) }, []) console.log(state) return <div>{JSON.stringify(state)}</div> }
内容的提问来源于stack exchange,提问作者Ooker
相关产品推荐
相关产品推荐

