【问题标题】: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 });
        },
      });

【问题讨论】:

  • 请注意,您传递给openModalcomponent 也将包含陈旧的closeModal

标签: reactjs typescript next.js use-context


【解决方案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 的地方添加代码
猜你喜欢
  • 2021-08-07
  • 1970-01-01
  • 2019-11-23
  • 1970-01-01
  • 1970-01-01
  • 2019-09-15
  • 1970-01-01
  • 2020-06-27
相关资源
最近更新 更多