Reactjs componentDidUpdate到挂钩的转换

Reactjs componentDidUpdate到挂钩的转换,reactjs,react-redux,material-ui,react-hooks,Reactjs,React Redux,Material Ui,React Hooks,我想把这个生命周期改写成一个钩子: componentDidUpdate = prevProps => { const { notifications, popNotification } = this.props; if (prevProps.notifications.length >= notifications.length) return; const [notification] = notifications; popNotifica

我想把这个生命周期改写成一个钩子:

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何时触发


在第一次加载或变量更改值时,将触发使用效果,如果不指定任何内容作为第二个参数,则每次重新渲染都将触发使用效果

若要用
useffect
钩子替换
componentdiddupdate
,请将第二个参数和一组变量传递给钩子,这些变量必须从该渲染更改为下一个渲染才能运行钩子

useEffect(() => {

  // your code

},[props.notifications])

两个问题一个问题我可以看到你的钩子代码:

  • usePrevious
    将永远不会更新超过第一个值集

    当在
    useffect
    中未设置依赖项时,它将运行一次,并且只运行一次。在您的情况下,由于要跟踪上一个值集,您需要使其成为
    useffect
    的依赖项,即
    useffect(()=>…,[value])
    ,这将强制回调重新运行并更新ref

    原来我误解了,当函数组件重新渲染时,它会重新运行挂钩的效果,您可以忽略第1点

  • useffect
    中的主更新代码与第1点类似,只运行一次

    同样,当通知计数发生更改时,代码将重新运行,因此需要设置为依赖项
    useffect(()=>…,[notifications.length])

  • 对于您提到的错误-
    通知未定义
    ,这表明您的
    通知
    状态不是有效数组或数组为空,请检查
    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]);
    

    您不需要在usePrevious的useEffect中添加
    [value]
    ?类似这样:
    useffect(()=>{…},[value])
    。同样在另一个useEffect中,我认为您应该在useEffect中添加
    [通知]
    。至少这个不应该比旧的多。看看这里的usePrevious:对不起,我错了。只需使用
    props.notifications
    。谢谢您更新答案并帮助我。我用了不同的方式,但两个版本都很好。你也可以删除你答案下的所有评论吗?为了更好的可读性,我已经删除了我的。@Peter我只是遵循了你在上面的
    if
    中使用的早期退出模式(FWIW我自己更喜欢这种方法:)
    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]);