294

Are there ways to simulate componentDidMount in React functional components via hooks?

jeyko
  • 3,093
  • 3
  • 13
  • 16

11 Answers11

580

For the stable version of hooks (React Version 16.8.0+)

For componentDidMount

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

For componentDidUpdate

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

For componentWillUnmount

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

So in this situation, you need to pass your dependency into this array. Let's assume you have a state like this

const [count, setCount] = useState(0);

And whenever count increases you want to re-render your function component. Then your useEffect should look like this

useEffect(() => {
  // <div>{count}</div>
}, [count]);

This way whenever your count updates your component will re-render. Hopefully this will help a bit.

Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141
Mertcan Diken
  • 14,483
  • 2
  • 26
  • 33
  • Thorough explanation! Is there a way to simulate componentDidReceiveProps? – jeyko Feb 13 '19 at 18:44
  • 1
    Thank you for this as I wasn't aware of the second argument in `useState`. To anyone reading this, please keep in mind that leaving the second argument `undefined` will cause your effect to trigger on every render (if I'm not mistaken). – dimiguel Mar 13 '19 at 19:17
  • Do you mean dependencies of useEffect (passing a dependency array)? If so yeah if you leave it empty that's a bug in the code. For array deconstruction, you need both [count, setCount] because count is your state variable in this example and setCount is your function to update that state. – Mertcan Diken Mar 13 '19 at 19:25
  • Thanks for the answer. The documentation about this is here https://reactjs.org/docs/hooks-effect.html – Josh Jan 19 '20 at 16:05
  • 3
    I've been trying to use the empty dependency array to simulate componentDidMount. The problem is that it usually results in a warning: "React Hook useEffect has a missing dependency: . Either include it or remove the dependency array react-hooks/exhaustive-deps". Applying either of the suggested "fixes" makes it no longer behave as componentDidMount. Am I doing something wrong? – Jonas Rosenqvist Aug 05 '20 at 22:50
  • 5
    This answer is is wrong in suggesting that it is an equivalent of componentDidMount (The question was asking for an _equivalent_), it only shows a workaround that works in some cases. A correct answer should state that there is no equivalent, and show recommended workarounds for all use cases. – kca Dec 20 '20 at 13:26
  • Answer doesn't claim that it's equal to anything there is a suggestion ends with hope it will help. So I can't see anything wrong here. And it seems like it helped a lot of people. But I'm open to any suggestions to improve the reliability of this answer since that is the whole purpose. @kca – Mertcan Diken Dec 21 '20 at 11:40
  • 9
    This is the most concise documentation of useEffect() on the internet. – nonethewiser Apr 09 '21 at 20:51
43

There is no exact equivalent for componentDidMount in react hooks.


In my experience, react hooks requires a different mindset when developing it and generally speaking you should not compare it to the class methods like componentDidMount.

With that said, there are ways in which you can use hooks to produce a similar effect to componentDidMount.

Solution 1:

useEffect(() => {
  console.log("I have been mounted")
}, [])

Solution 2:

const num = 5

useEffect(() => {
  console.log("I will only run if my deps change: ", num)
}, [num])

Solution 3 (With function):

useEffect(() => {
  const someFunc = () => {
    console.log("Function being run after/on mount")
  }
  someFunc()
}, [])

Solution 4 (useCallback):

const msg = "some message"

const myFunc = useCallback(() => {
  console.log(msg)
}, [msg])

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

Solution 5 (Getting creative):

export default function useDidMountHook(callback) {
  const didMount = useRef(null)

  useEffect(() => {
    if (callback && !didMount.current) {
      didMount.current = true
      callback()
    }
  })
}

It is worth noting that solution 5 should only really be used if none of the other solutions work for your use case. If you do decide you need solution 5 then I recommend using this pre-made hook use-did-mount.

Source (With more detail): Using componentDidMount in react hooks

Atomicts
  • 2,146
  • 2
  • 16
  • 13
17

There's no componentDidMount on functional components, but React Hooks provide a way you can emulate the behavior by using the useEffect hook.

Pass an empty array as the second argument to useEffect() to run only the callback on mount only.

Please read the documentation on useEffect.

function ComponentDidMount() {
  const [count, setCount] = React.useState(0);
  React.useEffect(() => {
    console.log('componentDidMount');
  }, []);

  return (
    <div>
      <p>componentDidMount: {count} times</p>
      <button
        onClick={() => {
          setCount(count + 1);
        }}
      >
        Click Me
      </button>
    </div>
  );
}

ReactDOM.render(
  <div>
    <ComponentDidMount />
  </div>,
  document.querySelector("#app")
);
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>
Yangshun Tay
  • 49,270
  • 33
  • 114
  • 141
6

useEffect() hook allows us to achieve the functionality of componentDidMount, componentDidUpdate componentWillUnMount functionalities.

Different syntaxes of useEffect() allows to achieve each of the above methods.

i) componentDidMount

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

ii) componentDidUpdate

useEffect(() => {
  //code here
}, [x,y,z]);

//where x,y,z are state variables on whose update, this method should get triggered

iii) componentDidUnmount

useEffect(() => {
  //code here
  return function() {
    //code to be run during unmount phase
  }
}, []);

You can check the official react site for more info. Official React Page on Hooks

General Grievance
  • 4,555
  • 31
  • 31
  • 45
4

Although accepted answer works, it is not recommended. When you have more than one state and you use it with useEffect, it will give you warning about adding it to dependency array or not using it at all.

It sometimes causes the problem which might give you unpredictable output. So I suggest that you take a little effort to rewrite your function as class. There are very little changes, and you can have some components as class and some as function. You're not obligated to use only one convention.

Take this for example

function App() {
  const [appointments, setAppointments] = useState([]);
  const [aptId, setAptId] = useState(1);

  useEffect(() => {
    fetch('./data.json')
      .then(response => response.json())
      .then(result => {
        const apts = result.map(item => {
          item.aptId = aptId;
          console.log(aptId);
          setAptId(aptId + 1);
          return item;
        })
        setAppointments(apts);
      });
  }, []);

  return(...);
}

and

class App extends Component {
  constructor() {
    super();
    this.state = {
      appointments: [],
      aptId: 1,
    }
  }

  componentDidMount() {
    fetch('./data.json')
      .then(response => response.json())
      .then(result => {
        const apts = result.map(item => {
          item.aptId = this.state.aptId;
          this.setState({aptId: this.state.aptId + 1});
          console.log(this.state.aptId);
          return item;
        });
        this.setState({appointments: apts});
      });
  }

  render(...);
}

This is only for example. so lets not talk about best practices or potential issues with the code. Both of this has same logic but the later only works as expected. You might get componentDidMount functionality with useEffect running for this time, but as your app grows, there are chances that you MAY face some issues. So, rather than rewriting at that phase, it's better to do this at early stage.

Besides, OOP is not that bad, if Procedure-Oriented Programming was enough, we would never have had Object-Oriented Programming. It's painful sometimes, but better (technically. personal issues aside).

Aniket Kariya
  • 1,471
  • 2
  • 21
  • 26
  • 2
    I did this. I faced issue using hooks. The issue was gone after converting it to class. – Julez Sep 21 '20 at 12:54
  • 2
    I have yet to see a useEffect 'gotcha' that could not be solved by refactoring the code - this example included. Using the callback version of `setState` or relocating the offending function out of the render cycle entirely will often do the trick - if not, your state is probably too complex and you need to implement your own reducer. Hooks aren't mandatory but it's clearly the future of React. I recommend reading this excellent [article on useEffect](https://overreacted.io/a-complete-guide-to-useeffect/) - it really helped me get my head around it when I started running into these issues. – lawrence-witt Feb 20 '21 at 01:36
3

Yes, there is a way to SIMULATE a componentDidMount in a React functional component

DISCLAIMER: The real problem here is that you need to change from "component life cycle mindset" to a "mindset of useEffect"

A React component is still a javascript function, so, if you want something to be executed BEFORE some other thing you must simply need to execute it first from top to bottom, if you think about it a function it's still a funtion like for example:

const myFunction = () => console.log('a')
const mySecondFunction = () => console.log('b)

mySecondFunction()
myFunction()

/* Result:
'b'
'a'
*/

That is really simple isn't it?

const MyComponent = () => {
const someCleverFunction = () => {...}

someCleverFunction() /* there I can execute it BEFORE 
the first render (componentWillMount)*/

useEffect(()=> {
  someCleverFunction() /* there I can execute it AFTER the first render */ 
},[]) /*I lie to react saying "hey, there are not external data (dependencies) that needs to be mapped here, trust me, I will leave this in blank.*/

return (
 <div>
   <h1>Hi!</h1>
 </div>
)}

And in this specific case it's true. But what happens if I do something like that:

const MyComponent = () => {
const someCleverFunction = () => {...}

someCleverFunction() /* there I can execute it BEFORE 
the first render (componentWillMount)*/

useEffect(()=> {
  someCleverFunction() /* there I can execute it AFTER the first render */ 
},[]) /*I lie to react saying "hey, there are not external data (dependencies) that needs to be maped here, trust me, I will leave this in blank.*/
return (
 <div>
   <h1>Hi!</h1>
 </div>
)}

This "cleverFunction" we are defining it's not the same in every re-render of the component. This lead to some nasty bugs and, in some cases to unnecessary re-renders of components or infinite re-render loops.

The real problem with that is that a React functional component is a function that "executes itself" several times depending on your state thanks to the useEffect hook (among others).

In short useEffect it's a hook designed specifically to synchronize your data with whatever you are seeing on the screen. If your data changes, your useEffect hook needs to be aware of that, always. That includes your methods, for that it's the array dependencies. Leaving that undefined leaves you open to hard-to-find bugs.

Because of that it's important to know how this work, and what you can do to get what you want in the "react" way.


const initialState = {
  count: 0,
  step: 1,
  done: false
};

function reducer(state, action) {
  const { count, step } = state;
  if (action.type === 'doSomething') {
    if(state.done === true) return state;
    return { ...state, count: state.count + state.step, state.done:true };
  } else if (action.type === 'step') {
    return { ...state, step: action.step };
  } else {
    throw new Error();
  }
}

const MyComponent = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const { count, step } = state;

useEffect(() => {
    dispatch({ type: 'doSomething' });
}, [dispatch]);

return (
 <div>
   <h1>Hi!</h1>
 </div>
)}

useReducer's dispatch method it's static so it means it will be the same method no matter the amount of times your component is re-rendered. So if you want to execute something just once and you want it rigth after the component is mounted, you can do something like the above example. This is a declarative way of do it right.

Source: The Complete Guide to useEffect - By Dan Abramov

That being said if you like to experiment with things and want to know how to do it "the imperative wat" you can use a useRef() with a counter or a boolean to check if that ref stores a defined reference or not, this is an imperative approach and it's recommended to avoid it if you're not familiar with what happen with react behind curtains.

That is because useRef() is a hook that saves the argument passed to it regardless of the amount of renders (I am keeping it simple because it's not the focus of the problem here, you can read this amazing article about useRef ). So it's the best approach to known when the first render of the component happened.

I leave an example showing 3 different ways of synchronise an "outside" effect (like an external function) with the "inner" component state.

You can run this snippet right here to see the logs and understand when these 3 functions are executed.

const { useRef, useState, useEffect, useCallback } = React

// External functions outside react component (like a data fetch)

function renderOnce(count) {
    console.log(`renderOnce: I executed ${count} times because my default state is: undefined by default!`);
}

function renderOnFirstReRender(count) {
    console.log(`renderOnUpdate: I executed just ${count} times!`);
}

function renderOnEveryUpdate(count) {
    console.log(`renderOnEveryUpdate: I executed ${count ? count + 1 : 1} times!`);
}

const MyComponent = () => {
    const [count, setCount] = useState(undefined);

    const mounted = useRef(0);

    // useCallback is used just to avoid warnings in console.log
    const renderOnEveryUpdateCallBack = useCallback(count => {
        renderOnEveryUpdate(count);
    }, []);

    if (mounted.current === 0) {
        renderOnce(count);
    }

    if (mounted.current === 1) renderOnFirstReRender(count);

    useEffect(() => {
        mounted.current = mounted.current + 1;
        renderOnEveryUpdateCallBack(count);
    }, [count, renderOnEveryUpdateCallBack]);

    return (
        <div>
            <h1>{count}</h1>
            <button onClick={() => setCount(prevState => (prevState ? prevState + 1 : 1))}>TouchMe</button>
        </div>
    );
};

class App extends React.Component {
    render() {
        return (
            <div>
                <h1>hI!</h1>
            </div>
        );
    }
}

ReactDOM.createRoot(
    document.getElementById("root")
).render(
  <MyComponent/>
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>

If you execute it you will see something like this: result of the snippet

2
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  // Similar to componentDidMount and componentDidUpdate:
  useEffect(() => {
    // Update the document title using the browser API
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Please visit this official docs. Very easy to understand the latest way.

https://reactjs.org/docs/hooks-effect.html

Pankaj Kumar
  • 422
  • 5
  • 10
1

Info about async functions inside the hook:

Effect callbacks are synchronous to prevent race conditions. Put the async function inside:

useEffect(() => {
  async function fetchData() {
    // You can await here
    const response = await MyAPI.getData(someId);
    // ...
  }
  fetchData();
}, [someId]); // Or [] if effect doesn't need props or state
zemil
  • 3,235
  • 2
  • 24
  • 33
0

useLayoutEffect hook is the best alternative to ComponentDidMount in React Hooks.

useLayoutEffect hook executes before Rendering UI and useEffect hook executes after rendering UI. Use it depend on your needs.

Sample Code:

import { useLayoutEffect, useEffect } from "react";

export default function App() {
  useEffect(() => {
    console.log("useEffect Statements");
  }, []);

  useLayoutEffect(() => {
    console.log("useLayoutEffect Statements");
  }, []);
  return (
    <div>
      <h1>Hello Guys</h1>
    </div>
  );
}
 
Smack Alpha
  • 1,828
  • 1
  • 17
  • 37
-1

You want to use useEffect(), which, depending on how you use the function, can act just like componentDidMount().

Eg. you could use a custom loaded state property which is initially set to false, and switch it to true on render, and only fire the effect when this value changes.

Documentation

markmoxx
  • 1,492
  • 1
  • 11
  • 21
  • 1
    This solution is not ideal. It's a bad idea to use a state value just to determine if component has mounted. Also, if you were to use a property, a ref would be better as it wouldn't trigger another re-render. – Yangshun Tay Dec 29 '18 at 23:10
-1

the exact equivalent hook for componentDidMount() is

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

hope this helpful :)

Aravinth
  • 105
  • 1
  • 2