Reactjs 在自定义钩子中使用ref是可行的还是建议对DOM进行修改?
我正在将一个第三方库合并到React应用程序中,hooks使这一切变得非常简单。 然而,我遇到了一些问题,我希望对“幕后”发生的事情有一些清晰的认识 为了简单起见,假设这是我的第三方代码,它直接改变了DOM:Reactjs 在自定义钩子中使用ref是可行的还是建议对DOM进行修改?,reactjs,react-hooks,Reactjs,React Hooks,我正在将一个第三方库合并到React应用程序中,hooks使这一切变得非常简单。 然而,我遇到了一些问题,我希望对“幕后”发生的事情有一些清晰的认识 为了简单起见,假设这是我的第三方代码,它直接改变了DOM: const renderStuff = (div, txt) => { if(div) div.innerHTML = txt; } 我的组件是这样的: export const EffectRender = () => { const divRef = useRef
const renderStuff = (div, txt) => {
if(div) div.innerHTML = txt;
}
我的组件是这样的:
export const EffectRender = () => {
const divRef = useRef();
useRenderer(divRef, "Hello, world");
return <div ref={divRef}></div>;
}
如果其中一个参数(在本例中为txt
)延迟更新,例如由于异步加载而延迟更新,则此操作有效。
但是当ref.current
值更改时,useffect
永远无法识别。
因此,如果在设置ref.current
之前设置了txt
(如本例所示),组件将永远不会渲染
我知道我可以通过在自定义挂钩中使用setState
来解决这个问题,如中所示。
但这开始让人感到麻烦
我还知道我可以将renderStuff
调用放在主组件上的useffect
钩子中,这保证了ref.current的设置。
所以:useffect(()=>{renderStuff(divRef.current,txt);}[txt])代码>很好
我的问题是,在定制钩子中使用ref的整个方法是否是一个好主意。
当ref发生变化时,有没有更简单的方法让钩子识别?
或者这是自定义挂钩不适合任务的情况吗?问题在于const div=ref.currentuserender
中的code>在钩子之外声明。在循环的这个时刻,ref仍然没有赋值,因此它的值是null
如果我正确理解了这个问题,那么解决方案就是将ref移动到useffect
回调中。这是你的建议之一,我相信这是正确的方式:
const useRenderer = (ref, txt) => {
useEffect(() => {
const div = ref.current;
renderStuff(div, txt);
}, [txt]);
};
useffect
当ref更改时,将不会触发依赖项。在您的情况下,这是正常的,因为当useffect
运行时,ref已经有了它的赋值。但是,如果ref发生了更改,并且您需要跟踪更改,那么您可以使用useState
感谢您澄清此问题。我想解释一下我的想法哪里出了问题,假设我不是唯一一个犯这个错误的人
我糟糕的逻辑
我们使用effect
钩子,因为代码正在改变DOM,而useffect
的存在正是为了处理这种副作用
我们使用ref
钩子连接到给定的DOM元素。ref
实例就像一个单例,因为该实例在应用程序的生命周期内不会改变。只有ref.current
属性更改
我在书中相关章节的正下方读到:
请记住,useRef的内容更改时不会通知您。
更改.current
属性不会导致重新渲染
由此,我了解到相关的依赖关系需要传递给useffect
调用。要更新的元素(在ref.current
中)就是这些依赖项之一。由于更改ref.current
属性不会触发重新渲染,因此可能也不会触发useffect
调用
顺便说一句:es lint
要求我将ref.current
(在我的例子中,div=ref.current
)添加到依赖项列表中,从而强化了这种想法:React Hook useffect缺少依赖项:“div”。要么包含它,要么删除依赖项数组。
当然,我总是信任linter
我的结论是:没有简单的方法可以使用效果
钩子渲染ref
实例。我需要以某种方式将ref.current
放入useState
设置器。丑
我的核心假设
我认为useffect
处理副作用的方式并不相关。effect
hook是一个埋在typescript
源代码内部的黑盒子,它以神秘的方式移动
但唯一被“埋葬”的是《圣经》中的这句话:
传递给useEffect的函数将在渲染完成后运行
致力于屏幕
“渲染后”。当然这就是useffect
处理副作用的方式,它保证在DOM准备就绪之前不会运行
解决方案
正如Alvaro的回答所建议的,确保阅读useffect
钩子中的ref.current
属性。ref
永远不会更改,并且ref.current
保证已经填充了DOM元素
像往常一样,事后看来这是显而易见的。再次感谢你,阿尔瓦罗。谢谢你的回答。太棒了!仅供参考:在您提到的提案中,我假设useffect
必须位于原始组件中。我开始明白我的想法哪里出了问题——假设useffect
必须在ref.current
设置时检测。但事实并非如此。我很高兴它奏效了!在钩子中使用useffect
,相当于将其直接放在组件中,但使用方式更方便/可共享。
const useRenderer = (ref, txt) => {
useEffect(() => {
const div = ref.current;
renderStuff(div, txt);
}, [txt]);
};