问题描述
我在功能组件中使用 react useRef
来获取 html 对象上的链接并将其存储在 Recoil atom 中。例如:
const Children = () => {
const [refLink,setSrefLink] = useRecoilState(refLink)
return <input ref={someRef}/>
}
const Parent = () => {
const [refLink,setSrefLink] = useRecoilState(refLink)
const someRef = useRef();
setSomeRef(someRef)
return <Children />;
}
export const refLink = atom({
key: 'refLink',default: null,});
但是当我的父组件卸载时出现错误:
react-dom.development.js:20997 未捕获的类型错误:无法分配给 文件中对象“#”的只读属性“当前” reac-dom.development.js
我无法想象这是什么问题;
解决方法
您不能只将 ref
作为道具传递。
通常,组件隐藏其实现,因此父组件不应该能够访问子组件创建的 DOM 元素。但在极少数情况下,您希望在组件上允许这样做,您必须使用 forwardRef
显式这样做:
const Children = React.forwardRef((props,someRef) => {
// −−−−−−−−−−−−−−−−−−^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return <input ref={someRef}/>
});
// −−^
const Parent = () => {
const someRef = useRef();
return <Children ref={someRef} />;
};
the documentation 中的更多内容。
,这里的问题是原子在默认情况下是冻结的(请参阅 documentation),并且 ref 通过改变对象的 current
属性来工作。
您可以通过传递 dangerouslyAllowMutability: true
来防止对象冻结。
export const refLinkState = atom({
key: 'refLink',default: null,dangerouslyAllowMutability: true,});
请注意,如果引用本身被另一个引用替换,这只会更新所有订阅者。如果 ref 使用者更改 current
属性,订阅者将不会重新呈现,因为 ref 对象仍然是同一个对象。
您可以通过不使用 ref 来解决此问题,而是将 ref 值直接传递到您的共享状态。
// without dangerouslyAllowMutability
export const refLinkState = atom({
key: 'refLink',});
const Children = () => {
const [refLink,setRefLink] = useRecoilState(refLinkState);
return <input ref={setRefLink} />;
};
在上面的场景中,我们已经完全消除了 refs,而是将 DOM 元素存储在没有 ref 包装器的反冲状态。
但是就像 forward refs documentation 提到的那样:
React 组件隐藏了它们的实现细节,包括它们的渲染输出。其他使用 FancyButton
的组件通常不需要 obtain a ref 到内部的 button
DOM 元素。这很好,因为它可以防止组件过度依赖彼此的 DOM 结构。
在不太了解结构以及您想要实现的目标的情况下,您可以例如提取 Child
中的相关数据并将其存储在共享状态中。但是如果我们有更多的上下文,可能会有更好的解决方案。