componentDidUpdate 转换为 Hooks

componentDidUpdate Conversion to Hooks

我想把这个生命周期方法重写成一个钩子:

componentDidUpdate = prevProps => {
    const { notifications, popNotification } = this.props;

    if (prevProps.notifications.length >= notifications.length) return;

    const [notification] = notifications;
    popNotification(notification.id);
    this.setState({
        open: true,
        message: notification.text,
        variant: notification.variant,
    });
};

我知道我必须使用 useEffect 挂钩,但它直到现在才开始工作。这是我到目前为止的想法:

function usePrevious(value) {
    const ref = useRef();
    useEffect(() => {
        ref.current = value;
    });
    return ref.current;
}

const dispatch = useDispatch();
const popMessage = useCallback(id =>
    dispatch(NotificationActions.popNotification(id))
);

const notifications = useSelector(state => state.notifications);
const previousValue = usePrevious(notifications.length);

useEffect(() => {
    if (previousValue >= notifications.length) return;

    // Extract notification from list of notifications
    const [notification] = notifications;
    popMessage(notification.id);
    // Open snackbar
    setSnackbar({
        open: true,
        message: notification.text,
        variant: notification.variant,
    });
});

此尝试与旧方法不同,它被调用的次数比旧方法多,并且还会抛出 TypeError: notification is undefined。另外,如果我在提取通知前加一个if (notifications.length > 0),还是不行。

您可以使用第二个参数(变量数组)限制 useEffect 何时触发。

useEffect(() => true, [variable]);

在第一次加载或变量改变值时,useEffect 将被触发,如果你没有指定任何东西作为第二个参数,每re-render useEffect 将被触发。

要将 componentDidUpdate 替换为 useEffect 挂钩,请将第二个参数传递给它,其中包含一个变量数组,必须从这个渲染更改为下一个渲染,以便挂钩 [=15] =]

useEffect(() => {

  // your code

},[props.notifications])

几个问题 我可以从你的 Hook 代码中看到一个问题:

  1. usePrevious 永远不会更新超过第一个值集

    如果在 useEffect 中没有设置任何依赖项,那么它 运行 只会出现一次。在你的情况下,因为你想跟踪最后一个值集,你需要使它成为 useEffect 的依赖项,即 useEffect(() => ..., [value]),这将强制回调到 re-run 并更新 ref .

    原来是我理解错了,函数组件re-renders时,re-run是hooks的效果,可以忽略第1点

  2. useEffect里面的主要更新代码,和第1点类似,只会运行一次。

    同样,当通知计数发生变化时,代码预计会 re-run,因此需要将其设置为依赖项 useEffect(() => ..., [notifications.length])

至于您提到的错误 - notification is undefined,这表明您的 notifications 状态不是有效数组或数组为空,请检查 useSelector(state => state.notifications) 的结果

根据您的代码,我希望它看起来像这样

const dispatch = useDispatch();
const popMessage = useCallback(id =>
  dispatch(NotificationActions.popNotification(id))
, []);

const notifications = useSelector(state => state.notifications);
const previousValue = usePrevious(notifications.length);

useEffect(() => {
  if (!notifications.length || previousValue >= notifications.length) return;

  // Extract notification from list of notifications
  const [notification] = notifications;
  if (!notification) return;

  popMessage(notification.id);
  // Open snackbar
  setSnackbar({
    open: true,
    message: notification.text,
    variant: notification.variant,
  });
}, [notifications]);