反应上下文(钩子)不更新所有引用
Posted
技术标签:
【中文标题】反应上下文(钩子)不更新所有引用【英文标题】:React context (hooks) not updating all references 【发布时间】:2021-10-26 23:33:31 【问题描述】:我正在尝试使用自定义关闭回调实现模式。我使用一个 useState 挂钩来完成此操作,该挂钩存储此方法并在已定义的 closeModal() 中执行它。 我的问题是,当我尝试从上下文中使用 closeModal 时,回调会保留初始值,而不是更新后的值。
function ModalProvider( children )
const [component, setComponent] = useState<React.ReactNode>(null);
const [styles, setStyles] = useState<React.CSSProperties>(null);
const [onClose, setOnClose] = useState<() => void>(null);
const openModal = (
component,
containerStyles,
onClose,
: OpenModalProps) =>
setComponent(component);
setStyles(containerStyles);
setOnClose(() => onClose);
;
const closeModal = () =>
onClose?.();
setComponent(null);
setStyles(null);
setOnClose(null);
;
return (
<ModalContext.Provider value= component, styles, openModal, closeModal >
children
</ModalContext.Provider>
);
所以这里我分两点使用,按钮和自定义钩子useClickOutside。当我单击按钮时,它可以正常工作,但是当从钩子中捕获事件时,会发生模式关闭但 onClose 未执行,因为它为空。
function Modal()
const component, styles, closeModal = useContext(ModalContext);
const contentRef = useRef<htmlDivElement>(null);
const [stopScrolling, continueScrolling] = useStopScrolling();
useClickOutside(contentRef, closeModal);
useEffect(() =>
if (!component) continueScrolling();
if (component) stopScrolling();
, [component]);
if (!component) return <></>;
return (
<>
<S.ModalWrapper>
<section style=styles ref=contentRef>
<button onClick=closeModal>x</button>
component
</section>
</S.ModalWrapper>
</>
);
function useClickOutside(ref, onClickOutside: () => void)
const handleClickOutside = (event) =>
if (ref.current && !ref.current.contains(event.target))
onClickOutside();
;
useEffect(() =>
document.addEventListener("mousedown", handleClickOutside);
return () =>
window.removeEventListener("mousedown", handleClickOutside);
;
, [ref]);
我在其他应用点上使用此方法也失败了,但我认为这足以发现问题。谢谢。
编辑
这就是我调用模态的方式
openModal(
component: <QuickView product=product close=closeModal />,
containerStyles:
width: "80%",
maxWidth: "1000px",
height: "auto",
backgroundColor: "transparent",
,
onClose: () =>
const as = router.asPath;
router.push(as, as, shallow: true );
,
);
【问题讨论】:
请注意,您传递给openModal
的component
也将包含陈旧的closeModal
。
【参考方案1】:
由于您的代码中有多个位置closeModal
在闭包中被捕获而没有被声明为依赖项,因此它有可能在变得陈旧后被调用。
在这种情况下,确保它永远不会有对onClose
的过时引用会更容易,您可以通过使用useRef()
而不是useState()
来定义它来做到这一点:
const [component, setComponent] = useState<React.ReactNode>(null);
const [styles, setStyles] = useState<React.CSSProperties>(null);
// const [onClose, setOnClose] = useState<() => void>(null);
const onCloseRef = useRef<() => void>(null);
const openModal = (
component,
containerStyles,
onClose,
: OpenModalProps) =>
setComponent(component);
setStyles(containerStyles);
// setOnClose(() => onClose);
onCloseRef.current = onClose;
;
const closeModal = () =>
// onClose?.();
onCloseRef.current?.();
setComponent(null);
setStyles(null);
// setOnClose(null);
onCloseRef.current = null;
;
【讨论】:
@MiguelRodriguez 您能否编辑您的问题并提供从上下文中使用并调用openModal
的组件的代码?
@MiguelRodriguez 我强烈建议使用eslint-plugin-react-hooks
linting 以自动检测此类过时的关闭问题。
我只是在调用 openModal 的地方添加代码以上是关于反应上下文(钩子)不更新所有引用的主要内容,如果未能解决你的问题,请参考以下文章
反应钩子:useState/context;无法读取未定义的属性“头像”/如何更新嵌套对象
如何将 React 钩子(useContext、useEffect)与 Apollo 反应钩子(useQuery)结合起来