Reactjs 在自定义钩子中使用ref是可行的还是建议对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

我正在将一个第三方库合并到React应用程序中,hooks使这一切变得非常简单。 然而,我遇到了一些问题,我希望对“幕后”发生的事情有一些清晰的认识

为了简单起见,假设这是我的第三方代码,它直接改变了DOM:

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]);
};