Component rendering multiple times without any reason - javascript

I was facing multiple re-renders and thus to find the issue I commented every line of code and at last found the constant definition causing re-render:
const MyComponent = () => {
console.log('render') // logs 4 times
const myRef = useRef(null)
return <h1>hello</h1>
}
const MyComponent = () => {
console.log('render') // logs 2 times
return <h1>hello</h1>
}
I know strict mode renders the component 2 times. But, why just defining myRef constant causing re-render further 2 times?
I just tried React.memo as comment, but it still renders 4 times:
export default React.memo(MyComponent)
Okay, I just tested using the same code in App.js and it only logged 2 times. So, it seems to be parent component issue. But my question is why in the child component without having anything causing renders additional 2 times just defining the variable?

As you and React docs said:
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
Source: https://reactjs.org/docs/strict-mode.html#detecting-unexpected-side-effects
It does not include useRef but it actually should be on this list because it uses same underlying mechanism as useState
React.memo won't help here because what causes re-render is React.StrictMode special mechanism to check side effects and other stuff, so it ignores memo and similar techniques.
What's interesting here that if you add useRef in Parent component it will invoke function body 2 times, but its children (if they don't use useRef) are gonna be invoked only once.
All this stuff only works for development mode, so in production there won't be any intentional double invocations and other things but they can still happen unintentionally.

Related

Is it okey to use side effects in the useState hook callback?

Imagine situation:
const [value, setValue] = useState(false);
const setSomething = (val) => {
setValue((prev) => {
fn(); dispatch(action); // or any other side effect
return prev + val;
});
};
Is it programmatically okey and fine with react principles to call side effects inside useState callback? May it affect the render process somehow?
It is not ok to use side effects inside the updater function. It might affect the render process, depending on the specific side effect.
It is not fine with react principles (separation of concerns, declarative code).
(I remember to have seen some exceptional use cases where putting some code inside the updater function was said to be the only solution, but I can't remember what it was. I'd appreciate an example in the comments.)
1. Consequences of using side effects
It is not ok to use side effects, basically for the same reasons why you shouldn't use side effects outside useEffect anywhere else.
Some side effects might affect the render process, other side effects might work fine (technically), but you are not supposed to rely on what happens inside the setter functions.
React guarantees that e.g. if you call setState( prev => prev + 1 ), then state would now be one more than before.
React does not guarantee what will happen behind the scenes to achieve that goal. React might call these setter functions multiple times, or not at all, and in any order:
StrictMode - Detecting unexpected side effects
... Because the above methods might be called more than once, it’s important that they do not contain side-effects. ...
2. following react principles
You should not put side effects inside the updater function, because it validates some principles, like separation of concerns and writing declarative code.
Separation of concerns:
setCount should do nothing but setting the count.
Writing declarative code:
Generally, you should write your code declarative, not imperative.
I.e. your code should "describe" what the state should be, instead of calling functions one after another.
I.e. you should write "B should be of value X, dependent on A" instead of "Change A, then change B"
In some cases React doesn't "know" anything about your side effects, so you need to take care about a consistent state yourself.
Sometimes you can not avoid writing some imperative code.
useEffect is there to help you with keeping the state consistent, by allowing you to e.g. relate some imperative code to some state, aka. "specifying dependencies".
If you don't use useEffect, you can still write working code, but you are just not using the tools react is providing for this purpose. You are not using React the way it is supposed to be used, and your code becomes less reliable.
Examples for problems with side effects
E.g. in this code you would expect that A and B are always identical, but it might give you unexpected results, like B being increased by 2 instead of 1 (e.g. when in DEV mode and strict mode):
export function DoSideEffect(){
const [ A, setA ] = useState(0);
const [ B, setB ] = useState(0);
return <div>
<button onClick={ () => {
setA( prevA => { // <-- setA might be called multiple times, with the same value for prevA
setB( prevB => prevB + 1 ); // <-- setB might be called multiple times, with a _different_ value for prevB
return prevA + 1;
} );
} }>set count</button>
{ A } / { B }
</div>;
}
E.g. this would not display the current value after the side effect, until the component is re-rendered for some other reason, like increasing the count:
export function DoSideEffect(){
const someValueRef = useRef(0);
const [ count, setCount ] = useState(0);
return <div>
<button onClick={ () => {
setCount( prevCount => {
someValueRef.current = someValueRef.current + 1; // <-- some side effect
return prevCount; // <-- value doesn't change, so react doesn't re-render
} );
} }>do side effect</button>
<button onClick={ () => {
setCount(prevCount => prevCount + 1 );
} }>set count</button>
<span>{ count } / {
someValueRef.current // <-- react doesn't necessarily display the current value
}</span>
</div>;
}
No, it is not ok to issue side-effects from a state updater function, it is to be considered a pure function.
The function return values are identical for identical arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams), and
The function application has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or input/output streams).
You may, or may not, be using the React.StrictMode component, but it's a method to help detect unexpected side effects.
Detecting unexpected side effects
Conceptually, React does work in two phases:
The render phase determines what changes need to be made to
e.g. the DOM. During this phase, React calls render and then
compares the result to the previous render.
The commit phase is
when React applies any changes. (In the case of React DOM, this is
when React inserts, updates, and removes DOM nodes.) React also calls
lifecycles like componentDidMount and componentDidUpdate during
this phase.
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.
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
Take a cue from the two highlighted bullet points regarding the intentional double-invoking of state updater functions and treat the state updater functions as pure functions.
For the code snippet you shared, I see no reason at all for the functions to be called from within the updater callback. They could/should be called outside the callback.
Example:
const setSomething = (val) => {
setValue((prev) => {
return prev + val;
});
fn();
dispatch(action);
};
I would not
Just because it works doesn't mean it's a good idea. The code sample you shared will function, but I wouldn't do it.
Putting unrelated logic together will confuse the next person who has to work with this code; very often, that "next person" is you: you, six months from now, after you've forgotten all about this code because you finished this feature and moved on. And now you come back and discover that some of the silverware has been stored in the bathroom medicine cabinet, and some of the linens are in the dishwasher, and all the plates are in a box labeled "DVDs".
I don't know how serious you are about the specific code sample you posted, but in case it's relevant: if you're using dispatch that means you've set up some kind of reducer, either with the useReducer hook, or possibly with Redux. If that's true, you should probably consider whether this boolean belongs in your Redux store, too:
const [ value, setValue ] = useState(false)
function setSomething(val) {
fn()
dispatch({ ...action, val })
}
(But it might not, and that's fine!)
If you're using actual Redux, you'll also have action-creators, and that's generally the correct place to put code that triggers side effects.
Regardless of whatever state tech you're using, I think you should prefer to avoid putting side-effect code into your individual components. The reason is that components are generally supposed to be reusable, but if you put a side-effect into the component that is not essential to display or interaction of the thing being visualized by the component, then you've just made it harder for other callers to use this component.
If the side-effect is essential to how this component works, then a better way to handle this would be to call setValue and the side-effect function directly instead of wrapping them up together. After all, you don't actually depend on the useState callback to accomplish your side-effect.
const [ value, setValue ] = useState(false)
function setSomething(val) {
setValue(value + val)
fn()
dispatch(action)
}

React: Difference between side effect in component's function vs effect?

I'm trying to understand the practical difference between having a side effect in a component function, vs having it within an effect which has no dependency array passed in (and as such should fire on every render). From what I can observe, they both run at the same frequency. I realize that effects allow for cleanup at the proper time, but I'm simply curious about the scenario where cleanup is not a factor.
The following CodePen shows what I'm talking about.
https://codepen.io/benrhere/pen/GRyvXZZ
The important piece is:
function EffectVsFunctionQuestion() {
const [count, setCount] = React.useState(0);
React.useEffect(()=>{
console.log("incremented within hook")
});
console.log("incremented within component function")
...
}
Side-effects issued from the useEffect hook have the benefit of being triggered at most once per render cycle. The render cycle here means the "commit phase" when React has computed the next view and commits it to the DOM.
This should not be confused with what React calls the "render phase" when it renders out the component (and children and entire ReactTree) to compute what changed and needs to be committed to the DOM.
The entire function body of a React function component is the "render" function. As you can see in the diagram, any unintentional side-effects in the render or body will occur during the "render phase" which can can be paused, aborted, or restarted (i.e. run again) by React. Note that the "render phase" is to be pure and free from side-effects.
The "commit phase" the component can work with the DOM and run side-effects.
Why does this matter? How to tell the difference.?
React actually ships with a StrictMode component that helps you detect unexpected side-effects.
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
Here's an example sandbox demo that demonstrates the unexpected side effects.
Note that the unexpected effect is doubled.
Code:
const externalValue1 = { count: 0 };
const externalValue2 = { count: 0 };
function EffectVsFunctionQuestion() {
const [state, setState] = React.useState(0);
React.useEffect(() => {
externalValue1.count++;
console.log("incremented within hook", externalValue1.count);
});
externalValue2.count++;
console.log("incremented within component function", externalValue2.count);
return (
<button type="button" onClick={() => setState((c) => c + 1)}>
Render
</button>
);
}
Essentially the useEffect hook ensures that code will not run until the DOM is mounted/updated. If you run code outside of it, it can be called before the DOM renders, which could lead to issues. It won't block content from rendering, since it's called after render. It's the equivalent of several of the previous class based lifecycle methods (ComponentDidMount, etc.). Docs

Is calling functions and creating new variables in react component body bad?

I've been working with react for years and I still have a simple question that I could never understand nor get the answer anywhere. Is calling an outside function in react component body bad?
For example:
import { getUser } from './helpers';
function MyComponent() {
const user = getUser();
return (
...
What will happen to user variable when component gets re-rendered by the parent component? Variables created in component body are created again in memory? Should I use useCallback or useMemo? I feel those two react functions are for other reasons, as expensive calculations and to prevent unecessary prop re-render.
To answer your first question in my experience I've never heard of calling outside functions within a react body as bad. In fact it's always been something that was encouraged especially for helper functions like in your example. There are definitely other ways to handle helper functions that may be needed multiple times (useContext comes to mind) but I don't see anything wrong with importing an outside function to call within a react body.
As far as what happens to user in your example it would get reset to the new return of getUser() upon any component re-render.
The user variable will be set to the value returned by getUser() on each re-render. And yet, getUser() will run on each re-render.
React Hooks are functions with use at the beginning of the function name, documented here.

useCallback: When to use it? Is there any drawback / performance issue?

There are articles related to what is useCallback and to use it when necessary. But in a big project it's very difficult to understand when to start using this.
So, if I use it for every use case, will it hit the performance of the application.
useCallback and useMemo are helper hooks with the main purpose off adding an extra layer of dependency check to ensure synchronicity. Usually you want to work with useCallback to ensure a stable signature to a prop which you know how will change and React doesn't.
A function(reference type) passed via props for example
const Component = ({ setParentState }) =>{
useEffect(() => setParentState('mounted'), [])
}
Lets assume you have a child component which uppon mounting must set some state in the parent (not usual), the above code will generate a warning of undeclared dependency in useEffect, so let's declare setParentState as a dependency to be checked by React
const Component = ({ setParentState }) =>{
useEffect(() => setParentState('mounted'), [setParentState])
}
Now this effect runs on each render, not only on mounting, but on each update. This happens because setParentState is a function which is recreated every time the function Component gets called. You know that setParentState won't change it's signature overtime so it's safe to tell React that. By wrapping the original helper inside an useCallback you're doing exactly that (by adding another dependency check layer).
const Component = ({ setParentState }) =>{
const stableSetter = useCallback(() => setParentState(), [])
useEffect(() => setParentState('mounted'), [stableSetter])
}
There you go. Now React knows that stableSetter won't change it's signature inside the lifecycle therefore the effect do not need run unecessarily.
On a side note useCallback it's also used like useMemo, to optmize expensive function calls (memoization).
The two main purposes of useCallback are
Optimize child components that rely on reference equality to prevent unnecessary
renders. Font
Memoize expensive computing calculations
Is there any drawback/performance?
useCallback is mainly used to optmize performance by changing stuff only when it's needed. It does that by adding a layer of dependencies just like useEffect (and similar to how React itself knows how something must change in the DOM), but as every single performance optimization technique, it can shoot backwards, if you fill your entire application with unecessary useCallback, useMemo, React.memo the performance will go the other way around.
So the key is to use wisely, carefully choosing what needs to have a stable signature and what doesn't.

Child component constructor called multiple times

I have a parent component which is a flat list which contains a header HeaderComponent. This HeaderComponent is a custom component that I have created which contains 2 child components of its own. Whenever i refresh the list, I am passing a boolean to the HeaderComponent as props which get passed onto its own children, I am doing this so I can check if each component needs to fetch new data or not. The problem is that whenever the parent refreshes and sets a new state the constructors of the child components get called everytime. Shouldn't the constructor be called only the first time the parent initializes and then all further calls involve calling the shouldComponentUpdate method of the children in order to see if it needs an update or not.
Parent component
_renderHeader = () => {
return <HeaderComponent Items={this.state.Data} refresh={this.state.refresh}/>;
};
render() {
console.log("TAG_RENDER render called " + this.state.refresh);
return (
<FlatList
refreshing={this.state.refresh}
onRefresh={() => {
console.log("onRefresh");
this.setState({
refresh: true
}, () => {
this._fetchData();
});
}}
......
ListHeaderComponent={() => this._renderHeader()}
.......
/>
);
}
Header Component
export default class HeaderComponent extends React.Component {
constructor(props) {
super(props);
console.debug("HeaderComponent");
}
render() {
return (
<MainHeader Items={this.props.Items}/>
<SubHeader refresh={this.props.refresh}/>
);
}
}
The constructor of MainHeader and Subheader gets called whenever the parent component refreshes. Does this mean that it is creating new child components each time it refreshes because I can see the render of the children also being called multiple times.
Control your index.js file. If you see <React.StrictMode>, you should change to <>. This is solved my problem.
It should be like:
ReactDOM.render(
<>
<App/>
</>,
document.getElementById('root')
);
As correctly stated in the one of the answers , removing the strict mode fixes the issue. Coming to why it does that, its because the strict mode intentionally calls the 'render' method twice in order to detect potential problems.
React works in two phases:render and commit. Render phase checks and determines the new changes to be applied. And commit phase applies it.
Render phase lifecycle includes methods like : constructor, UNSAFE_componentWillMount,UNSAFE_componentWillReceiveProps, ...,render and few more.
The render phase is time consuming and is often broken into pieces to free up the browser. Render phase might be called multiple times before the commit phase(usually very fast).
Since the render phase methods are called more than once, its important that none of those method have any problems or side effects.
Thus just in order to highlight the possible side effects to make them easy to spot, react explicitly double invoke the render phase methods.
You can read more about this on :https://reactjs.org/docs/strict-mode.html#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
https://reactjs.org/docs/strict-mode.html
As stated in the site,
Note:
This only applies to development mode. Lifecycles will not be double-invoked in production mode.

Categories