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



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

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. 我收到错误 - "重新渲染太多。 完整代码:代码沙盒

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

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

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

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

没有依赖关系的效果会导致"Too many re-renders.":它在每次渲染后运行,然后调用setIsLoading来更新状态(loading),这将导致组件重新渲染,这将再次运行effectsetState将再次调用,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不存在,并且 可选链接在这里可以很方便 ->props.match?.params?.id

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

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

最新更新