在做记账-react项目时遇到的问题hook effect:
每次渲染后,从React Hook useEffect内部对'localTags'变量的分配将丢失。为了保存值随时间推移,将其存储在useRef挂钩中,并将可变值保留在'.current'属性中。否则,您可以移动此变量直接在useEffect内部
修改前代码:
const useTags =() =>{//封装一个自定义hook
let localTags = JSON.parse(window.localStorage.getItem('tags') || '[]');
const [tags,setTags] = useState<{id:number ; name:string}[]>(localTags);
useEffect(()=>{
if (localTags.length ===0){
localTags = [
{id:createId(),name:'衣服'},
{id:createId(),name:'吃饭饭'},
{id:createId(),name:'家庭消费'},
{id:createId(),name:'粗去玩~'},
]
}
setTags(localTags);
},[]);//deps是空数组代表第一次进来就执行
修改后代码:
const useTags =() =>{//封装一个自定义hook
let localTags = JSON.parse(window.localStorage.getItem('tags') || JSON.stringify([
{"id":createId(),"name":"衣服"},
{"id":createId(),"name":"吃饭饭"},
{"id":createId(),"name":"家庭消费"},
{"id":createId(),"name":"粗去玩"}
]));
const [tags,setTags] = useState<{id:number ; name:string}[]>(localTags);
useEffect(()=>{
//setTags(localTags)
setTags((localTags)=>([...localTags]));
},[]);//deps是空数组代表第一次进来就执行
传入空的依赖数组 [ ],意味着hook只在组件挂载时运行一次,不会再重新渲染,可我在useEffect函数的内部setTags(localTags) 用的值 'localTags' 是在函数外面声明的,在setTags(localTags) 回调时,localTags 的值不会发生改变,因为在执行effect 的时候,我们会创建一个闭包,并且将localTags的值保存在闭包中,可是在effect外部的localTags是在变化的,这时候TS就会报错,会问你你里面的setTags(localTags)中的localTags是在变化的,可是你的依赖值却是空。
可是我就是只要一次渲染!!!!因为这个是我需要的初始值,只需要渲染一次
方法:
就是将你setTags(localTag)中的值改为一个函数 如下所示
setTags((localtags)=>([...localtags]))
还有一个例子帮助理解:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1); // 这个 effect 依赖于 `count` state
}, 1000);
return () => clearInterval(id);
}, []); // Bug: `count` 没有被指定为依赖
return <h1>{count}</h1>;
}
传入空的依赖数组 [],意味着该 hook 只在组件挂载时运行一次,并非重新渲染时。但如此会有问题,在 setInterval 的回调中,count 的值不会发生变化。因为当 effect 执行时,我们会创建一个闭包,并将 count 的值被保存在该闭包当中,且初值为 0。每隔一秒,回调就会执行 setCount(0 + 1),因此,count 永远不会超过 1。
指定 [count] 作为依赖列表就能修复这个 Bug,但会导致每次改变发生时定时器都被重置。事实上,每个 setInterval 在被清除前(类似于 setTimeout)都会调用一次,但这并不是我们想要的。要解决这个问题,我们可以使用 setState 的函数式更新形式。它允许我们指定 state 该 如何 改变而不用引用 当前 state:
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1); // ✅ 在这不依赖于外部的 `count` 变量
}, 1000);
return () => clearInterval(id);
}, []); // 我们的 effect 不适用组件作用域中的任何变量
return <h1>{count}</h1>;
}
(setCount 函数的身份是被确保稳定的,所以可以放心的省略掉)
此时,setInterval 的回调依旧每秒调用一次,但每次 setCount 内部的回调取到的 count 是最新值(在回调中变量命名为 c)。