It's very common to require some setup when a component is mounted to perform tasks like network calls. Before hooks were introduced we were taught to use functions like componentDidMount(). It is natural to look for equivalent hooks when transitioning to functional components.
TL;DR, hooks and lifecycle methods are based on very different principles. Methods like componentDidMount() revolve around lifecycles and render time whilst hooks are designed around state and synchronization with the DOM.
A lot of programmers assume that they can replace the behavior of componentDidMount() with useEffect(fn, ). While there don't seem to be any major errors when using this approach, it can still lead to some app-breaking bugs. Both methods are fundamentally different, and you might not get the expected behavior you want. Programmers are not supposed to think of hooks as functions that run when a component mounts. Assuming hooks work in this fashion will hinder your understanding (and learning) of hooks.
State and props are captured differently
Perhaps the most obvious difference is how the two methods capture state and props. This becomes especially apparent when using async methods.
This component seems quite simple. Once it has mounted, it called a function which returns a promise that resolves after some time. Once that promise is resolved it then logs the current state of the name variable. Let's try porting the same code over to a functional component.
The above code doesn't work properly. The useEffect() method captures the values of state and props when it is created. As a result, the console will print an empty line, even though the user could've typed in anything at this point. To tell React that the effect should use the most up-to-date value, you must pass dependencies directly into your effects. The same logic applies to props. In this case, effects are simpler than a class-based component, since they would also have to use the componentDidUpdate() method.
The methods are called at different times
React can determine when state has been set synchronously within the componentDidMount() method. Let's look at the actual lifecycle of a component:
The component is mounted
The DOM is created with the returned content from render()
componentDidMount() is called and the state is updated
The DOM is re-rendered and the content is updated
One might expect that we should see a flicker between the first and second frames but that's not the case. React detects that the state had been updated and only displays the second frame. This can be useful if a component requires the proportions of an element that can only be calculated when the DOM is rendered.
Hooks and useEffect() both run after the component is mounted. The difference is that hooks are also run after the DOM content has been painted. So, if the state is updated synchronously within an effect method users will see a flicker as the first frame is replaced with the second frame.
You can obtain the old behavior with hooks by using the useLayoutEffect() method, which is called before the content is committed to the page. However, most apps don't need to use this hook and most programmers should stick to useEffect().
Class-based components are designed around lifecycles and time. Functional classes instead aim to synchronize state with the DOM. Failing to shift mindsets can result in some strange quirks and bugs which could be difficult to solve without proper knowledge. In short, one should consider “based on the state, what should my component look like, and when should it re-render.” These questions will ensure that your functional components run properly.