Why useEffect re-rendering when nothing has changed? - reactjs

I have 2 react functional components like this :
const Test = function (props) {
console.log("rendered");
useEffect(() => {}, []);
return <div>Testing</div>;
}
const Parent = function () {
return <div>Component: <Test /></div>;
}
As we can see above, there is no state or props change still browser console showing "rendered" twice but if i am commenting the useEffect it'll print only once. I tried to google it but didn't find any proper reason of this.

This is due to React.StrictMode.
From the React Docs - Detecting unexpected side effects
Strict mode can’t automatically detect side effects for you, but it
can help you spot them by making them a little more deterministic.
This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer
When building your project for production, this should not happen.

It's because of React.StrictMode. If there's no useEffect (or any statefulness), react might not render it twice in StrictMode because it doesn't need to. StrictMode renders components twice to make sure that everything works properly and there are no deprecated lifecycle methods or other practices that are problematic. It's whole purpose is to make sure that your application will work well with the more stringent requirements for concurrent mode. It only runs these checks in development mode so as not to impact performance in your production builds.
In this CodeSandbox, you can see that the strict-mode renders twice for the same components, while the normal mode doesn't.

Related

UseEffect is called twice in component mounting even when using useCallback

I have the following problem. I have a component which needs to call an API when mounted. I do the call in a useCallback like this:
const sendCode = useCallback(() => {
console.log('InsideSendCode');
}, []);
And then I call this function inside of a useEffect like this:
useEffect(() => {
sendCode();
}, [sendCode]);
The thing is that, even by using the useCallback, the message is displayed in the console twice and I've seen that this would be the only option.
I know about the StrictMode, but I don't want to disable it.
If everyone would have an opinion, would be great.
Thanks.
That is standard behavior in Strict mode:
When Strict Mode is on, React mounts components twice (in development only!) to stress-test your Effects.
Initially react was only calling render twice to detect if you have some side effects, but afterwards they also added calling effects twice during initial mount, to make sure you have implemented cleanup functions well. This only applies to strict mode AFAIK.
I suggest you read the link above. You have few options:
if the API call you are making is GET request which simply gets some information, let it be called twice, there is no much harm.
You could use a ref to keep track if it is first mount or not, and then correspondingly make request in useEffect only if this is first mount. Although this approach is not listed in official recommendations, I suppose you can use it as a last resort; it was documented at some point. Dan Abramov also mentioned you could use it as last measure, just they don't encourage it.
disable Strict mode

React hooks queries

I have the following queries -
What's the difference between useEffect, useMemo and useCallback hooks ? I have gone through many examples and explanations but still am not clear on their difference. All I know is that each executes only when at least one of their dependencies change.
If useRef hook allows us to persist with values between re-renders, why not use a simple variable (not a state) for the same ? I read somewhere that if not changed manually, useRef will have the same value all the time. Can't we achieve this with a simple variable ?
tl;dr
useMemo fired immediately, useCallback not.
local variables a not persisted between renders
Explained
useMemo and useCallback area really very same. The difference between them are mentioned in the hooks names.
useMemo is created for calculating some heavy things (like taking some very long list and mapping it in another) and storing it for some time – as documentation says React can drop useMemo result and make hook to run again. When component is rendering first time, all useMemos continuously runs calculations and their results may be used during the render. When rendering next times (if no hook dependencies changed) React do not call passed function but just using memorized result.
useCallback is created just for preserving variable references to functions that is passed as first argument. It is very helpful when callback, created with that hook is passed to some children components cause persistent variable reference do not invalidates memorized components.
Small example:
const app = () => {
console.log('app render starts')
const title = React.useMemo(() => {
console.log('running calcualtion!')
return 'Hello world'
}, [])
console.log('app render continues')
const handleClick = React.useCallback(() => {
console.log('handling click')
}, [])
console.log('app render continues again')
return <div onClick={handleClick}>{title}</div>
}
/*
Output after mounting app:
- app render starts
- running calcualtion!
- app render continues
- app render continues again
And after clicking div:
- handling click
*/
About useRef
React functional components are functions that runs again on every component render. Without hooks that functions are totally pure and unable to contain any state or preserve variables value – all function-scoped variables are created on every render again.
Hooks know which component are currently being rendered, so hooks able to store some data about component and get it back when component re-rendered.
In lot of cases useRef really are just a way to persist value between renders. As described above, you can't achieve that with simple variables inside of component's function. It could be achieved with some global variable declared outside of component. It even may be better choice if variable value do not depends one component mount/unmount which are handled by useRef.

Why useState cause re-render so many times

I am investigating why the useState cause render so many time like the following app, or here
import React from "react";
export default function App() {
const [name, setName] = React.useState("unknown");
console.log("render...", name);
React.useEffect(() => {
const doIt = async () => {
await new Promise(res => setTimeout(res, 1000));
setName("Ron");
};
doIt();
}, []);
return <div className="App">{name}</div>;
}
I think it should render 2 times, 1 is for initial, plus the useEffect. But why it actually render 4 times as below?
render... unknown
render... unknown
render... Ron
render... Ron
The console statement is in the function body, React will execute the function body on each render.
On component mount, As the init state is empty, the console will print an empty string.
As you are updating the state on component Mount, React will execute the function body again and then log the state with the updated value.
As you are using the React.StrictMode it can render the component more than once. That is the reason you see the console logs multiple times.
The commit phase is usually very fast, but rendering can be slow. For this reason, the upcoming concurrent mode (which is not enabled by default yet) breaks the rendering work into pieces, pausing and resuming the work to avoid blocking the browser. This means that React may invoke render phase lifecycles more than once before committing, or it may invoke them without committing at all (because of an error or a higher priority interruption).
Render phase lifecycles include the following class component methods:
constructor
componentWillMount (or UNSAFE_componentWillMount)
componentWillReceiveProps (or UNSAFE_componentWillReceiveProps)
componentWillUpdate (or UNSAFE_componentWillUpdate)
getDerivedStateFromProps
shouldComponentUpdate render
setState updater functions (the first argument)
Because the above methods might be called more than once, it’s important that they do not contain side-effects. Ignoring this rule can lead to a variety of problems, including memory leaks and invalid application state. Unfortunately, it can be difficult to detect these problems as they can often be non-deterministic.
You can read more about React.StrictMode here
This is because you are doing setState in useEffect without any dependency. So whenever you component is rendering, the use effect is working as componentDidMount and then inside of this, there is a setState which cause another rerender and again it calls componentDidmount. Its kinda looping the whole process.

React render twice with hooks

why my component is rendering twice?
export default function App() {
console.log("asd");
const [count, setCount] = useState(0);
return (
<div>
<Title count={count} />
<button
onClick={() => {
setCount(count + 1);
}}
/>
</div>
);
}
//console= "asd" "asd"
its rendering two times, but if I remove the useState it does not happen
Your app might be wrapped by React.StrictMode. StrictMode is a tool for highlighting potential problems in an application.
StrictMode currently helps with:
Identifying components with unsafe lifecycles
Warning about legacy string ref API usage
Warning about deprecated findDOMNode usage
Detecting unexpected side effects
Detecting legacy context API
Strict mode can’t automatically detect side effects for you, but it can help you spot them by making them a little more deterministic. This is done by intentionally double-invoking the following functions:
Class component constructor, render, and shouldComponentUpdate methods
Class component static getDerivedStateFromProps method
Function component bodies (your app is functional component)
State updater functions (the first argument to setState)
Functions passed to useState, useMemo, or useReducer
More Detail
For React 18: In development mode, components are remounted once! Handle it in useEffect with a cleanup function for stale responses.
from the link posted below, written by core react team member:
One reason is that if you're running with Strict Mode we now remount
each component once during development. (If this seems pretty
invasive, consider that we also remount every time you save a file in
development anyway—to pick up your code edits. This behavior was on
for two years by now, so it's not new.) This normally shouldn't break
your code if your code ignores stale responses. If it doesn't, it
means you have race conditions that need to be fixed — here is how to
fix them by adding a cleanup function. If you used an external cache
(or implemented it yourself), you wouldn't even see the duplicate
requests. But dev-only duplicate requests aren't harmful if your code
can handle the responses arriving out of order. (If they are too
annoying, you can remove Strict Mode completely, but I don’t see why
you'd need to.)
https://www.reddit.com/r/reactjs/comments/vi6q6f/what_is_the_recommended_way_to_load_data_for/

Will derived state be allowed for React Concurrent Mode?

Today we can use getDerivedStateFromProps for class components and state updates during render phase for React Hooks/function components to create derived state, if needed.
I am especially curious about the React Hook variant: Will this be allowed in React Concurrent Mode?
If I wrap the ScrollView component from the FAQ with StrictMode <React.StrictMode>...</React.StrictMode>, there will be no warnings. However, I still have a slight feeling in stomach, this pattern could be problematic with concurrent mode.
So question is:
Is derived state still possible in React Concurrent Mode with regard to the Hooks FAQ link?
Example:
function Comp( {someCond} ) {
const [counter, setCounter] = useState(0);
if (someCond) {
// we are setting derived state directly in render (see FAQ).
setCounter(prev => prev + 1);
// <-- imagine, at this point React aborts this render (and restarts later)
// Does it lead to inconsistencies?
}
// ... return some jsx
}
It's safe, but you can only update the state of this component until you have no side-effect on other components during render. in react 16.13.0 a warning added about this.
react 16.13.0 release note:
Warnings for some updates during render A React component should not
cause side effects in other components during rendering.
It is supported to call setState during render, but only for the same
component. If you call setState during a render on a different
component, you will now see a warning:
Warning: Cannot update a component from inside the function body of a different component.

Resources