简体   繁体   English

将生命周期方法反应到钩子

[英]React lifecycle methods to hooks

I have simple blog with articles.我有简单的博客文章。 And I want to rewrite classes to functional components and hooks.我想将类重写为功能组件和钩子。 Now I got this logic in lifecycle methods for my page with edit/add form: it works fine.现在,我在具有编辑/添加表单的页面的生命周期方法中得到了这个逻辑:它工作正常。

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;
}

I rewrote it all to hooks like that:我把它全部改写成这样的钩子:

//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. I got error - "Too many re-renders."我收到错误 - “重新渲染太多。” at codesandbox full code: codesandbox在codesandbox完整代码: codesandbox

Its strange, but at localhost there is no error "Too many re-renders."这很奇怪,但在本地主机上没有错误“重新渲染太多”。

  1. Don't know what to do with my class "shouldComponentUpdate" method how to rewrite it to hooks.不知道如何处理我的 class “shouldComponentUpdate” 方法如何将其重写为钩子。 Tryed 'memo' but have no idea how to write in in this case.尝试了“备忘录”,但不知道在这种情况下如何写。

  2. And anyway I'm missing something, because it all won't work - it's not updating form fields properly.无论如何,我错过了一些东西,因为这一切都行不通——它没有正确更新表单字段。

If you have a good knowledge with react hooks please help, or give some advice - how to fix it?如果您对反应挂钩有很好的了解,请提供帮助或提供一些建议 - 如何解决?

The effect without dependency is causing "Too many re-renders."没有依赖的效果会导致"Too many re-renders." : it runs after every render then it calls setIsLoading to update the state( loading ) which will cause the component to re-render, which will run the effect again and the setState will be called again and effect and so on... :它在每次渲染后运行,然后调用setIsLoading来更新状态( loading ),这将导致组件重新渲染,这将再次运行effect ,并且将再次调用setStateeffect等等......

//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);
  })

to fix the issue either remove the setIsLoading from it, or add IsLoading as dependency.要解决此问题,请从中删除setIsLoading ,或将IsLoading添加为依赖项。

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

you can also merge two mount effects into one like this ( yours is also working, but I think this is stylistically preferable):您也可以像这样将两个安装效果合并为一个(您的也可以使用,但我认为这在风格上更可取):

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

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

for the second bullet: about rewriting your component's shouldComponentUpdate ;第二个要点:关于重写组件的shouldComponentUpdate first I must point that your existing shouldComponentUpdate isn't sound reasonable, since you always return true , and you are only using it to trigger loading state;首先我必须指出你现有的shouldComponentUpdate听起来不合理,因为你总是返回true ,你只是用它来触发loading state; and its not eligible to be written with React.memo (which is ~equivalent of shouldComponentUpdate in class components);并且它不符合使用React.memo编写的条件(这相当于shouldComponentUpdate组件中的 shouldComponentUpdate ); so you only need something to be executed on every props change to determine loading state and for that you can use an effect with props as its dependency like this:所以你只需要在每次道具更改时执行一些操作来确定loading state 并且为此你可以使用带有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 

based on my realization this shall do the job, ( despite having hard time understanding why you write the logic this way in the first place) and if it's not doing fine you may tune the logic a little bit to match your needs, you get the idea how the props change effect works.根据我的认识,这应该可以完成工作,(尽管很难理解为什么首先要以这种方式编写逻辑),如果做得不好,您可以稍微调整逻辑以满足您的需求,您会得到了解道具更改效果如何工作。 also you many need to handle typeError in case id , params or match doesn't exist and Optional chaining can be handy tool here -> props.match?.params?.id如果idparamsmatch不存在,您也需要处理typeError ,并且可选链接在这里可以是方便的工具-> props.match?.params?.id

If you miss the old lifecycle hooks, you could always recreate them as custom hooks:如果你错过了旧的生命周期钩子,你总是可以将它们重新创建为自定义钩子:

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)
}

Usage (your example refactored):用法(您的示例已重构):

  //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();
  });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM