hook组件中拆分时间图的注意事项
时间:2023-03-28 16:26:22
HTML
最近遇到一个bug,问题出在reacthook中,为了代码的可读性,我将一个view拆分成多个子组件,其中有一些都是纯组件,有的是带useState的有状态组件,但是在实践过程中发现,当父组件使用setState更新视图时,有的有状态组件中的state会自动恢复到初始值,有的会不仔细对比,发现组件实例化的写法不一样。代码概要如下:import{useState}from"react";importCChildfrom"./Child";import"./styles.css";exportdefaultfunctionApp(){const[random,setRandom]=useState(()=>Math.random()*10);constRenderPart=function(){const[count,setCount]=useState(0);return(
{count}setCount((prev)=>prev+1)}>加一个
);};constrenderOtherPart=function(){const[count,setCount]=useState(0);}return(
{count}setCount((prev)=>prev+1)}>add2one
);};return(
{random}
{setRandom(Math.random()*10);}}>刷新{/*通过其他具有组件样式的渲染函数渲染组件会刷新子组件的状态*/}{/*通过其他具有函数样式的渲染函数渲染组件也不会刷新状态*/}{renderOtherPart()} );}使用Babel转译后的代码:使用
的方式"usestrict";functionCChild(){const[count,setCount]=useState(0);返回/*#__PURE__*/React.createElement("div",null,count,/*#__PURE__*/React.createElement("button",{onClick:()=>setCount(prev=>prev+1)},"加一个"));}functionApp(){const[random,setRandom]=useState(()=>Math.random()*10);constRenderPart=function(){//返回CChild();返回/*#__PURE__*/React.createElement(CHild,null);};返回/*#__PURE__*/React.createElement("div",{className:"App"},/*#__PURE__*/React.createElement("h3",null,random),/*#__PURE__*/React.createElement("button",{onClick:()=>{setRandom(Math.random()*10);}},"refresh"),/*#__PURE__*/React.createElement(RenderPart,null));}使用renderOtherPart的方式"usestrict";functionCChild(){const[count,setCount]=useState(0);返回/*#__PURE__*/React.createElement("div",null,count,/*#__PURE__*/React.createElement("button",{onClick:()=>setCount(prev=>prev+1)},"加一个"));}functionApp(){const[random,setRandom]=useState(()=>Math.random()*10);constrenderOtherPart=function(){//返回CHild();返回/*#__PURE__*/React.createElement(CHild,null);};返回/*#__PURE__*/React.createElement("div",{className:"App"},/*#__PURE__*/React.createElement("h3",null,random),/*#__PURE__*/React.createElement("button",{onClick:()=>{setRandom(Math.random()*10);}},"refresh"),renderOtherPart());}网上复现代码地址:通过运行代码,它可以发现,以
的方式实例化组件会刷新子组件的状态,而{renderChild()}则不会。原因如下:翻译自babel从结果来看,不同的是在使用
时多了一个React.createElement(RenderPart),而React.createElement(Child)只在RenderPart中使用。与使用{renderOtherPart()}的方式相比,React.createElement(Child)只使用了一次,没有RenderPart中间层。正是因为多了一个RenderPart,因为它在hook组件中。当父组件设置State时,会重新创建RenderPart,内存地址会发生变化。react的diff时,判断旧的组件被删除,然后添加。触发更新逻辑的新组件。为了验证这个问题,我将RenderPart从hook组件中提取到外部保持不变,或者使用useCallBack或useMemo缓存RenderPart,结果验证并没有重置子组件的状态。constRenderPart=function(){const[state,setState]=useState(()=>{console.log("RenderPart初始状态");return0;});//返回CHild();console.log("renderPartRenderPart");return(
{state}
setState((prev)=>prev+1)}>setstate);};导出默认函数App(){const[random,setRandom]=useState(()=>Math.random()*10);return(
{random}
{setRandom(Math.random()*10);}}>刷新{/*通过其他具有组件样式的渲染函数渲染组件会刷新子组件的状态*/});}或者使用useCallBack:functionApp(){const[random,setRandom]=useState(()=>Math.random()*10);constRenderPart=useCallback(function(){return;},[]);return({random}
{setRandom(Math.random()*10);}}>刷新{/*通过其他具有组件样式的渲染函数渲染组件会刷新子组件的状态*/}