How to use componentWillMount() in React Hooks?

杀马特。学长 韩版系。学妹 提交于 2019-11-29 19:42:47
Bhaskar Gyan Vardhan

You cannot use any of the existing lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount etc.) in a hook.They can only be used in a class components. And Hooks you can only use in a functional components. What the below line from the React doc

If you’re familiar with React class lifecycle methods, you can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined.

suggest is, you can mimic these lifecycle method from class component in a functional components.

Code inside componentDidMount run once when the component is mounted. useEffect hook equivalent for this behaviour is

useEffect(() => {
  // Your code here
}, []);

Notice the second parameter here (empty array). This will run only once.

Without the second parameter the useEffect hook will be called on every render of the component which can be dangerous.

useEffect(() => {
  // Your code here
});

componentWillUnmount is use for cleanup (like removing event listners, cancel the timer etc). Say you are adding a event listner in componentDidMount and remvoing it in componentWillUnmount as below.

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}

Hook equivalent of above code will be as follows

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])
MING WU

According to reactjs.org, componentWillMount will not be supported in the future. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

There is no need to use componentWillMount.

If you want to do something before the component mounted, just do it in the constructor().

If you want to do network requests, do not do it in componentWillMount. It is because doing this will lead to unexpected bugs.

Network requests can be done in componentDidMount.

Hope it helps.


updated on 08/03/2019

The reason why you ask for componentWillMount is probably because you want to initialize the state before renders.

Just do it in useState.

const helloWorld=()=>{
    const [value,setValue]=useState(0) //initialize your state here
    return <p>{value}</p>
}
export default helloWorld;

or maybe You want to run a function in componentWillMount, for example, if your original code looks like this:

componentWillMount(){
  console.log('componentWillMount')
}

with hook, all you need to do is to remove the lifecycle method:

const hookComponent=()=>{
    console.log('componentWillMount')
    return <p>you have transfered componeWillMount from class component into hook </p>
}

I just want to add something to the first answer about useEffect.

useEffect(()=>{})

useEffect runs on every render, it is a combination of componentDidUpdate, componentDidMount and ComponentWillUnmount.

 useEffect(()=>{},[])

If we add an empty array in useEffect it runs just when the component mounted. It is because useEffect will compare the array you passed to it. So it does not have to be an empty array.It can be array that is not changing. For example, it can be [1,2,3] or ['1,2']. useEffect still only runs when component mounted.

It depends on you whether you want it to run just once or runs after every render. It is not dangerous if you forgot to add an array as long as you know what you are doing.

I created a sample for hook. Please check it out.

https://codesandbox.io/s/kw6xj153wr


updated on 21/08/2019

It has been a white since I wrote the above answer. There is something that I think you need to pay attention to. When you use

useEffect(()=>{},[])

When react compares the values you passed to the array [], it uses Object.is() to compare. If you pass a object to it, such as

useEffect(()=>{},[{name:'Tom'}])

This is exactly the same as:

useEffect(()=>{})

It will re-render every time because when Object.is() compares an object, it compares its reference not the value itself. It is the same as why {}==={} returns false because their references are different. If you still want to compare the object itself not the reference, you can do something like this:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])

useLayoutEffect could accomplish this with an empty set of observers ([]) if the functionality is actually similar to componentWillMount -- it will run before the first content gets to the DOM -- though there are actually two updates but they are synchronous before drawing to the screen.

for example:


function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (<div>some content</div>);
}

The benefit over useState with an initializer/setter or useEffect is though it may compute a render pass, there are no actual re-renders to the DOM that a user will notice, and it is run before the first noticable render, which is not the case for useEffect. The downside is of course a slight delay in your first render since a check/update has to happen before painting to screen. It really does depend on your use-case, though.

I think personally, useMemo is fine in some niche cases where you need to do something heavy -- as long as you keep in mind it is the exception vs the norm.

useComponentDidMount hook

In most cases useComponentDidMount is the tool to use. It will run only once, after component has mounted(initial render).

 const useComponentDidMount = func => useEffect(func, []);

useComponentWillMount

It is important to note that in class components componentWillMount is considered legacy. If you need code to run only once before component has mounted, you can use the constructor. More about it here. Since functional component doesn't have the equivelant of a constructor, using a hook to run code only once before component mounts might make sense in certain cases. You can achieve it with a custom hook.

const useComponentWillMount = func => {
  const willMount = useRef(true);

  if (willMount.current) {
    func();
  }

  useComponentDidMount(() => {
    willMount.current = false;
  });
};

However, there is a pitfall. Don't use it to asynchronously set your state (e.x following a server request. As you might expect it to affect the initial rendering which it won't). Such cases should be handled with useComponentDidMount.

Demo

const Component = (props) => {
  useComponentWillMount(() => console.log("Runs only once before component mounts"));
  useComponentDidMount(() => console.log("Runs only once after component mounts"));
  ...

  return (
    <div>{...}</div>
  );
}

Full Demo

I wrote a custom hook that will run a function once before first render.

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

export default (fun) => {
  const [hasRendered, setHasRendered] = useState(false)

  useEffect(() => setHasRendered(true), [hasRendered])

  if (!hasRendered) {
    fun()
  }
}

Usage:

import React, { useEffect } from 'react'
import useBeforeFirstRender from '../hooks/useBeforeFirstRender'


export default () => (
  useBeforeFirstRender(() => {
    console.log('Do stuff here')
  })

  return (
    <div>
      My component
    </div>
  )
)

https://reactjs.org/docs/hooks-reference.html#usememo

Remember that the function passed to useMemo runs during rendering. Don’t do anything there that you wouldn’t normally do while rendering. For example, side effects belong in useEffect, not useMemo.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!