【问题标题】:React lifecycle methods to hooks将生命周期方法反应到钩子
【发布时间】:2021-03-27 04:53:01
【问题描述】:

我有简单的博客文章。我想将类重写为功能组件和钩子。 现在,我使用编辑/添加表单在页面的生命周期方法中获得了这个逻辑: 它工作正常。

componentDidUpdate(prevProps, prevState) {
 if (this.props.match.params.id !== prevProps.match.params.id) {
    if (prevProps.match.params.id) {
        this.props.onUnload();
      }

      this.id = this.props.match.params.id;
        if (this.id) {
            return this.props.onLoad(userService.articles.get(this.id));
        }
        this.props.onLoad(null);
   }
   this.isLoading = false;
}

componentDidMount() {
  if (this.id) {
    this.isLoading = true;
    return this.props.onLoad(userService.articles.get(this.id));
  }
  this.isLoading = false;
  this.props.onLoad(null);
}
   
componentWillUnmount() {
   this.props.onUnload();
}
   
shouldComponentUpdate(newProps, newState) {
   if (this.props.match.params.id !== newProps.match.params.id) {
      this.isLoading = true;
   }
   return true;
}

我把它全部改写成这样的钩子:

//componentDidMount
  useEffect(() => {
    if (id) {
      setIsloading(true);
      return props.onLoad(userService.articles.get(id));
    }
    setIsloading(false);
    props.onLoad(null);
  }, []);

  useEffect(()=> {
      prevId.current = id;
      }, [id]
  );

  //componentDidUpdate
  useEffect(() => {
    //const id = props.match.params.id;
    if (id !== prevId.current) {
      if (prevId.current) {
        props.onUnload();
      }
      if (id) {
        return props.onLoad(userService.articles.get(id));
      }
      props.onLoad(null);
    }
    setIsloading(false);
  });

  //componentWillUnmount
  useEffect(() => {
     return props.onUnload();
  }, []);

  1. 我收到错误 - “重新渲染太多。”在密码箱 完整代码:codesandbox

这很奇怪,但是在本地主机上没有错误“重新渲染太多。”

  1. 不知道如何处理我的类“shouldComponentUpdate”方法如何将其重写为钩子。尝试了“备忘录”,但不知道在这种情况下如何写。

  2. 无论如何我都遗漏了一些东西,因为这一切都不起作用 - 它没有正确更新表单字段。

如果您对 React hooks 有很好的了解,请提供帮助或提供一些建议 - 如何解决?

【问题讨论】:

  • 阅读更多useEffect钩子的工作原理。 reactjs.org/docs/…
  • 泰。我做到了,并且在我的代码中,当需要它时,我对 useEffect 有依赖关系。我错过了什么吗?
  • 是的,我看到几个useEffects 没有任何依赖关系。
  • 这可能对您有所帮助? stackoverflow.com/questions/54551949/…
  • 我只看到没有 deps 的“componentDidUpdate( )”,因为它应该每次都运行。我错了,它也应该有部门吗?

标签: javascript reactjs react-hooks


【解决方案1】:

没有依赖的效果导致"Too many re-renders.":它在每次渲染之后运行然后它调用setIsLoading来更新状态(loading)这将导致组件重新渲染,这将运行effect再次调用setState 并再次调用effect 等等...

//componentDidUpdate
  useEffect(() => {
    //const id = props.match.params.id;
    if (id !== prevId.current) {
      if (prevId.current) {
        props.onUnload();
      }
      if (id) {
        return props.onLoad(userService.articles.get(id));
      }
      props.onLoad(null);
    }
    setIsloading(false);
  })

要解决此问题,请从中删除 setIsLoading,或添加 IsLoading 作为依赖项。

//componentDidUpdate
  useEffect(() => {
    ...
    setIsloading(false);
  },[isLoading]);

你也可以像这样将两个坐骑效果合并为一个(你的也可以,但我认为这在风格上更可取):

//componentDidMount
  useEffect(() => {
    if (id) {
      setIsloading(true);
      return props.onLoad(userService.articles.get(id));
    }
    setIsloading(false);
    props.onLoad(null);

    //componentWillUnmount
    return function () {
      props.onUnload()
    }
  }, []);

第二个要点:关于重写组件的shouldComponentUpdate;首先我必须指出你现有的shouldComponentUpdate 听起来不合理,因为你总是返回true,而你只是用它来触发loading 状态;并且它不符合使用React.memo 编写的条件(在类组件中~相当于shouldComponentUpdate);所以你只需要在每次 props 更改时执行一些操作来确定 loading 状态,为此你可以使用带有 props 的效果作为它的依赖项,如下所示:

//manually keeping old props id
const prevPropsId = useRef();

useEffect(() => {
   if (prevPropsId.current !== props.match.params.id) {
      setIsLoading(true);
   }
   prevPropsId.current = props.match.params.id;
 }, [props.match.params.id]);
// this hook only run if `props.match.params.id` change 

根据我的认识,这应该可以完成工作,(尽管很难理解为什么首先要以这种方式编写逻辑),如果它做得不好,您可以稍微调整逻辑以满足您的需求,你知道道具改变效果是如何工作的。你也需要处理typeError,以防idparamsmatch不存在并且 Optional chaining 在这里可以成为方便的工具 -> props.match?.params?.id

【讨论】:

  • 谢谢。我做了改变。现在我没有错误。但我对 useEffect 的部门有很多警告。它仍然没有像我的旧类代码生命周期方法所做的那样:(
  • 我认为这可能是因为我仍然无法为我的旧“shouldComponentUpdate”编写钩子,尝试使用“备忘录”尝试不同的东西,但没有任何效果。
  • @WillBlack 查看关于重写shouldComponentUpdate的更新
  • 再次感谢。我做了您发送的所有更改,但它仍然无法正常工作。我想我只是在我的代码中遗漏了一些简单的东西,只是想不通:(。没有错误,没有警告。你能在codeandbox看到代码吗?codesandbox.io/s/immutable-silence-9prc4?file=/src/components/…
【解决方案2】:

如果您错过了旧的生命周期挂钩,您可以随时将它们重新创建为自定义挂钩:

function useComponentDidMount(effect) {
  useEffect(() => {
    effect();
  }, []);
}
function useComponentWillUnmount(effect) {
  useEffect(() => {
    return effect;
  }, []);
}
function useComponentDidUpdate(effect, dependencies) {
  const hasMountedRef = useRef(false);
  const prevDependenciesRef = useRef(null)
  useEffect(() => {
    // don't run on first render, only on subsequent changes
    if (!hasMountedRef.current) {
      hasMountedRef.current = true;
      prevDependenciesRef.current = dependencies;
      return;
    }
    // run effect with previous dependencies, like in componentDidUpdate!
    effect(...prevDependenciesRef.current || []);
    prevDependenciesRef.current = dependencies;
  }, dependencies)
}

用法(重构您的示例):

  //componentDidMount
  useComponentDidMount(() => {
    if (id) {
      setIsloading(true);
      props.onLoad(userService.articles.get(id));
      return;
    }
    setIsloading(false);
    props.onLoad(null);
  });

  //componentDidUpdate
  useComponentDidUpdate((prevId) => {
    if (prevId) {
      props.onUnload();
    }
    if (id) {
      props.onLoad(userService.articles.get(id));
      return;
    }
    props.onLoad(null);
    setIsloading(false);
  }, [id]);

  //componentWillUnmount
  useComponentWillUnmount(() => {
     props.onUnload();
  });

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-01-15
    • 2017-11-22
    • 1970-01-01
    • 1970-01-01
    • 2021-08-21
    • 2018-03-13
    • 2018-08-30
    相关资源
    最近更新 更多