React 上下文(挂钩)不更新所有引用
React context (hooks) not updating all references
我正在尝试使用自定义关闭回调实现模式。我使用一个 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 });
},
});
由于您的代码中有多个地方 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;
};
我正在尝试使用自定义关闭回调实现模式。我使用一个 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 });
},
});
由于您的代码中有多个地方 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;
};