了解 useState 钩子行为

时间:2021-05-17 10:06:41

标签: javascript reactjs

我目前正在我的应用程序中实现涟漪效应。我使用 Material UI 按钮 (https://material-ui.com/components/buttons/) 作为起点。

在我自己的实现过程中,我遇到了一些我不确定的行为。我已经创建了一个相应的沙箱,其中包含一个基本示例 https://codesandbox.io/s/zealous-cloud-69uvh?file=/src/App.js

Container 是父组件,Ripple 是子组件。 Container 具有 onMouseUponMouseDown 处理程序,分别添加和删除子组件。

下面是 onMouseDown 逻辑的一小段摘录。 rippleCounter 变量是一个 ref 对象,它保留一个计数,用于设置 keyRipple

    setRipples((oldRipples) => {
      return [
        ...oldRipples,
        <Ripple
          key={rippleCounter.current}
          timeout={500}
          rippleX={rippleX}
          rippleY={rippleY}
          rippleSize={rippleSize}
        />
      ];
    });
    rippleCounter.current += 1;
  };

我了解 useState 是异步的,因此无法保证针对 key 组件设置的 Ripple 会在其递增之前或之后。这是我在理解上遇到一些困难的地方。

示例 2 遵循此异步行为,因为 Ripple 组件在之前或之后设置了其 key 值。由于在某些情况下实现了相同的密钥,这会导致涟漪混乱。

示例 1 似乎没有遵循此行为。 setRipples 函数始终以同步方式运行,rippleCounter 始终在 setRipples 函数运行后递增。即永远不会使用相同的密钥。

示例 1 在移除子组件之前管理和更新状态。

const Ripple = ({
  in: inProp,
  onExited = () => {},
  timeout,
  rippleSize,
  rippleY,
  rippleX
}) => {
  const [leaving, setLeaving] = useState(false);

  const style = {
    width: rippleSize,
    height: rippleSize,
    top: -(rippleSize / 2) + rippleY,
    left: -(rippleSize / 2) + rippleX
  };

  useLayoutEffect(() => {
    if (!inProp) {
      setLeaving(true);

      const timeoutValue = setTimeout(onExited, timeout);

      return () => {
        clearTimeout(timeoutValue);
      };
    }
  }, [timeout, inProp, onExited]);

  return (
    <span style={style} className="ripple">
      <span className={"ripple-child" + (leaving ? " leaving" : "")}></span>
    </span>
  );
};

示例 2 没有被管理的状态。

const Ripple = ({
  in: inProp,
  onExited = () => {},
  timeout,
  rippleSize,
  rippleY,
  rippleX
}) => {
  // const [leaving, setLeaving] = useState(false);

  const style = {
    width: rippleSize,
    height: rippleSize,
    top: -(rippleSize / 2) + rippleY,
    left: -(rippleSize / 2) + rippleX
  };

  useLayoutEffect(() => {
    if (!inProp) {
      // setLeaving(true);

      const timeoutValue = setTimeout(onExited, timeout);

      return () => {
        clearTimeout(timeoutValue);
      };
    }
  }, [timeout, inProp, onExited]);

  return (
    <span style={style} className="ripple">
      <span className={"ripple-child" + (!inProp ? " leaving" : "")}></span>
    </span>
  );
};

为不好的表达道歉。如果我可以提供其他信息,请告诉我。

0 个答案:

没有答案