Your output is as it should be.
I think you are confused about the output because you think that useEffect is same as componentDidMount but that is not correct. They both are different, couple of important differences between them are mentioned below:
They run at different times
(related to your question)
They both are called after the initial render of the component but useEffect is called after the browser has painted the screen whereas componentDidMount is called before the screen is painted by the browser.
Capturing props and state
(not related to your question, feel free to skip to the end of the answer)
useEffect captures the state and props whereas componentDidMount doesn't do this.
Consider the following code snippets to understand what useEffect captures the state and props means.
class App extends React.Component {
constructor() {
super();
this.state = {
count: 0
};
}
componentDidMount() {
setTimeout(() => {
console.log('count value = ' + this.state.count);
}, 4000);
}
render() {
return (
<div>
<p>You clicked the button { this.state.count } times</p>
<button
onClick={ () => this.setState(prev => ({ count: prev.count + 1 })) }>
Increment Counter
</button>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
function App() {
const [count, setCount] = React.useState(0);
React.useEffect(() => {
setTimeout(() => {
console.log('count value = ' + count);
}, 4000);
}, [])
return (
<div>
<p>You clicked the button { count } times</p>
<button
onClick={ () => setCount(count + 1) }>
Increment Counter
</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Both code snippets are same except first one has class based component and second one has functional component.
These both snippets have a variable named count in the state and they both log the value of the count variable to the console after 4 seconds. They also include a button which can be used to increment the value of the count.
Try to click the button (4 or 5 times) before the value of the count is logged on the console.
If you thought that componentDidMount and useEffect are same then you might be surprised to see that both code snippets log different values of count variable after 4 seconds.
Class based code snippet logs the latest value whereas functional component based code snippet logs the initial value of count variable.
Reason why they log different value of count variable is because:
this.state inside class component always points to latest state, so it logs the latest value of count after 4 seconds.
useEffect captures the initial value of the count variable and logs the captured value instead of the latest value.
For in-depth explanation of the differences between useEffect and componentDidMount, i suggest you read following articles
Coming back to your question
If you paid attention to the first part of my answer that is related to your question, you probably now understand why useEffect runs its callback after both First and Second components have mounted.
If not, then let me explain.
After the execution of useCustomHook function that was called from within the First component, First component is mounted and if it was a class based component, its componentDidMount lifecycle function would have been called at this point.
After First component has mounted, Second component mounts and if this too was a class based component, its componentDidMount lifecycle function would have been called at this point.
After both components have mounted, browser paints the screen and as a result, you see the output on the screen. After the browser has painted the screen, callback function of the useEffect gets executed for both First and Second component.
In short, useEffect lets the browser paint the screen before running its effect/callback. That is why useEffect gets called is logged at at the end of the output.
You can see more details about this on official docs: Timing of effects
If you turn First and Second component in to class components, then output will be as:
1. component First rendering
2. component Second rendering
3. component First mounted. // console.log statement inside componentDidMount
4. component Second mounted. // console.log statement inside componentDidMount
You might expect 3rd line to be at 2nd place and 2nd line at 3rd place but that's not the case because react first executes the render functions of all the child components before they are inserted in the DOM and only after they are inserted in the DOM, componentDidMount of each component executes.
If you create Third and Fourth components and create following hierarchy of class components:
App
|__ First
| |__ Third
| |__ Fourth
|
|__ Second
then you will se the following output:
1. First component constructor
2. component First rendering
3. Third component constructor
4. component Third rendering
5. Fourth component constructor
6. component Fourth rendering
7. Second component constructor
8. component Second rendering
9. component Fourth mounted
10. component Third mounted
11. component First mounted
12. component Second mounted