在React的官方文档中提到了-
如果你熟悉React类的生命周期方法,你可以思考 将useEffect钩子作为componentDidMount, componentDidUpdate和 componentWillUnmount总和。
我的问题是-我们如何在钩子中使用componentWillMount()生命周期方法?
在React的官方文档中提到了-
如果你熟悉React类的生命周期方法,你可以思考 将useEffect钩子作为componentDidMount, componentDidUpdate和 componentWillUnmount总和。
我的问题是-我们如何在钩子中使用componentWillMount()生命周期方法?
当前回答
这是我如何使用useRef钩子模拟函数组件中的构造函数的方式:
function Component(props) {
const willMount = useRef(true);
if (willMount.current) {
console.log('This runs only once before rendering the component.');
willMount.current = false;
}
return (<h1>Meow world!</h1>);
}
下面是生命周期的例子:
function RenderLog(props) {
console.log('Render log: ' + props.children);
return (<>{props.children}</>);
}
function Component(props) {
console.log('Body');
const [count, setCount] = useState(0);
const willMount = useRef(true);
if (willMount.current) {
console.log('First time load (it runs only once)');
setCount(2);
willMount.current = false;
} else {
console.log('Repeated load');
}
useEffect(() => {
console.log('Component did mount (it runs only once)');
return () => console.log('Component will unmount');
}, []);
useEffect(() => {
console.log('Component did update');
});
useEffect(() => {
console.log('Component will receive props');
}, [count]);
return (
<>
<h1>{count}</h1>
<RenderLog>{count}</RenderLog>
</>
);
}
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props
当然,Class组件没有Body步骤,由于函数和类的概念不同,不可能进行1:1的模拟。
其他回答
有一个很好的解决方案来实现componentDidMount和componentWillUnmount与useEffect。
根据文档,useEffect可以返回一个“清理”函数。该函数不会在第一次useEffect调用时调用,只在后续调用时调用。
因此,如果我们使用useEffect钩子而没有任何依赖关系,那么该钩子只会在组件挂载时被调用,而“cleanup”函数则会在组件卸载时被调用。
useEffect(() => {
console.log('componentDidMount');
return () => {
console.log('componentWillUnmount');
};
}, []);
只有在卸载组件时才调用清理返回函数调用。
希望这能有所帮助。
根据reactjs.org的说法,componentWillMount在未来将不受支持。 https://reactjs.org/docs/react-component.html#unsafe_componentwillmount
不需要使用componentWillMount。
如果你想在组件挂载之前执行一些操作,只需在构造函数()中执行即可。
如果你想做网络请求,不要在componentWillMount中做。这是因为这样做会导致意想不到的错误。
网络请求可以在componentDidMount中完成。
希望能有所帮助。
2019年8月3日更新
你请求componentWillMount的原因可能是因为你想在呈现之前初始化状态。
就在我们的地盘上做吧。
const helloWorld=()=>{
const [value,setValue]=useState(0) //initialize your state here
return <p>{value}</p>
}
export default helloWorld;
或者你想在componentWillMount中运行一个函数,例如,如果你的原始代码看起来像这样:
componentWillMount(){
console.log('componentWillMount')
}
使用hook,你所需要做的就是删除生命周期方法:
const hookComponent=()=>{
console.log('componentWillMount')
return <p>you have transfered componeWillMount from class component into hook </p>
}
我只是想对第一个关于useEffect的答案补充一些东西。
useEffect(()=>{})
useEffect运行在每次渲染上,它是componentDidUpdate, componentDidMount和ComponentWillUnmount的组合。
useEffect(()=>{},[])
如果我们在useEffect中添加一个空数组,它只在组件挂载时运行。这是因为useEffect将比较您传递给它的数组。 所以它不一定是空数组。它可以是一个不变的数组。例如,它可以是[1,2,3]或['1,2']。useEffect仍然只在组件挂载时运行。
这取决于你是想让它只运行一次,还是在每次渲染后运行。如果您忘记添加数组,只要您知道自己在做什么,这并不危险。
我为hook创建了一个示例。请查看一下。
https://codesandbox.io/s/kw6xj153wr
2019年8月21日更新
我写上面的答案已经有一段时间了。有件事我觉得你需要注意。 当你使用
useEffect(()=>{},[])
当react比较传递给数组[]的值时,它使用Object.is()进行比较。 如果您将一个对象传递给它,例如
useEffect(()=>{},[{name:'Tom'}])
这和:
useEffect(()=>{})
每次都会重新呈现,因为object .is()比较对象时比较的是对象的引用,而不是值本身。这和为什么{}==={}返回false是一样的,因为它们的引用不同。 如果你仍然想比较对象本身而不是引用,你可以这样做:
useEffect(()=>{},[JSON.stringify({name:'Tom'})])
2021年7月9日更新:
关于依赖关系的一些更新:
一般来说,如果你使用一个函数或一个对象作为依赖项,它总是会重新呈现。但是react已经为您提供了解决方案:useCallback和useMemo
useCallback能够记住一个函数。 useMemo能够记住一个对象。
请看这篇文章:
https://javascript.plainenglish.io/5-useeffect-infinite-loop-patterns-2dc9d45a253f
我们最近遇到了这个问题,因为我们需要在组件挂载时做一些事情,也就是我们需要更新全局状态。
所以我创建了这个钩子,不确定这个方法有多好,但到目前为止,只要我们少用它,只用于简单的任务,它就能工作。我可能不会将它用于网络请求和其他长时间运行的复杂任务。
import { useRef } from 'react';
function useComponentWillMount(callback: () => void) {
const hasMounted = useRef(false);
if (!hasMounted.current) {
(() => {
hasMounted.current = true;
callback();
})();
}
console.log(hasMounted.current);
return null;
}
export default useComponentWillMount;
只需要简单地在useEffect中添加一个空的依赖数组,它将作为componentDidMount工作。
useEffect(() => {
// Your code here
console.log("componentDidMount")
}, []);
useLayoutEffect可以用一个空的观察者集([])来完成这一点,如果功能实际上类似componentWillMount——它将在第一个内容到达DOM之前运行——尽管实际上有两个更新,但它们在绘制到屏幕之前是同步的。
例如:
function MyComponent({ ...andItsProps }) {
useLayoutEffect(()=> {
console.log('I am about to render!');
},[]);
return (<div>some content</div>);
}
与useState相比,使用初始化器/setter或useEffect的好处是,尽管它可以计算一个渲染传递,但用户不会注意到DOM的实际重新渲染,并且它是在第一次明显的渲染之前运行的,而useEffect则不是这样。缺点当然是在你的第一次渲染中有轻微的延迟,因为在绘制到屏幕之前必须进行检查/更新。不过,这确实取决于您的用例。
我个人认为,useMemo在一些特定的情况下是很好的,当你需要做一些沉重的事情时——只要你记住这是例外与规范。