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.
Related
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.
My functional component is structured as follows:
// Initialization
useEffect(() => {
fetchDetailedUserInfo();
}, []);
// Update on initialization of detailedUserInfo
useEffect(() => {
if (detailedUserInfo) {
//...
setInitialFormValues(..);
}
}, [detailedUserInfo]);
// Update on initialization of initialFormValues
useEffect(() => {
//...
}, [initialFormValues]);
// Return JSX
return ( .. );
What determines when the render (return) runs in between all these useEffects, and how are the useEffects ordered among themselves? What is the exact flow?
According to docs:
By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.
The clean-up function runs before the component is removed from the UI to prevent memory leaks. Additionally, if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect. In our example, this means a new subscription is created on every update. To avoid firing an effect on every update, refer to the next section.
A lot of people tend to miss the above statement on the documentation.
There are two behaviours to be noticed.
When there is an empty dependency array []
In this case the cleanup effect will only get called when the component is going to unmount
When there is some dependency in the dep array
In this case, the cleanup effect is called everytime the useEffect is triggered due to the update phase, it will be called first, then the callback function will be called, so as to ensure that any cleanup is done before the next callback is called.
The main reason for this confusion is that when someone comes from class components this is not the case, and they tend to follow the lifecycle diagram provided.
The hooks LC diagram is not really official, and has a small flaw
https://github.com/Wavez/react-hooks-lifecycle/issues/5
I had raised an issue on the github repo as well with the correction.
Its still open though.
Do checkout the docs here
https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect
why dont you do something like this instead?
useEffect(() => {
// you can return the promise inside the fetchDetailedUserInfo function, and ensure that you return the reponse. make sure to add a catch block or handle that in the response.
fetchDetailedUserInfo()
.then(res=>{
setInitialFormValues(...)
})
}, []);
The first useEffect runs when the component is first rendered, so will fire first. The second useEffect will fire whenever detailedUserInfo has changed its value (assuming its not null/false, etc.). The third useEffect will fire whenever initialFormValues has changed its value.
I'm trying to do an ajax call when a state changes, and then set another state to the result of that ajax call.
const [plc, setPlc] = useState(null);
const [tags, setTags] = useState([]);
...
useEffect(()=>{
if(plc != null) {
myAjaxPromiseFunction(plc).catch((err)=>{
console.log(err);
}).then((tags)=>{
setTags(tags);
});
}
}, [plc]);
For some reason, this results in an infinite loop. However, when I remove the setTags(tags); statement it works as expected. It appears that the setTags function is causing the effect hook to update, but tags is not a part of the dependencies of this effect hook.
FYI, the tags variable is supposed to be a list of objects.
EDIT:
I have one other useEffect in my code.
useEffect(() => { // Update which tags are logged
if(plc != null) {
anotherAjaxFunction(plc, tags).catch(err => {
toaster.show({
message: err,
intent: "danger"
});
}).then(tags => {
setTags(tags);
});
}
}, [plc, updateLoggedFlag]);
However, this useEffect is not dependant on tags, and the issue still occurs if I remove this block of code.
Side note: The updateLoggedFlag is a variable I was planning to use to force the effect to update as needed, but I haven't used it anywhere yet.
Another EDIT: Link to reproduction using code sandbox: https://codesandbox.io/s/cranky-ives-3xyrq?file=/src/App.js
Any ideas? Thanks for the help.
In short: move TagColumn outside of App, as it is here.
The problem is not connected to the effects declaration, it's due to the component declaration.
In your sandbox, the TagColumn was defined inside of App. It means that every time App was rendered (in particular, every time its state is changed), the const TagColumn was assigned a different value. React can't see that it is the same component every time - after all, it's not really the same, since the captured scope is changing.
So, App renders a new TagColumn. React sees that component is created from scratch, renders it, and, in particular, invokes its hooks, including every useEffect. Inside useEffect, you use callback to change the state of App. App rerenders, creates yet another component TagColumn, which is again rendered for the first time in its life, calling every useEffect... and the cycle continues.
In general, you shouldn't capture anything non-constant in the functional component scope. Instead:
if the value is generated by external context (App, in this case) - pass it as props;
if the value is generated by the component itself - use useState, useRef or other hooks, as necessary.
The problem I think with your code is that it does not include tags in the dependency array. Conceptually, this should work fine, as mentioned before by other developers, but React documentation states that, all the state variables and props used inside the callback, should be present inside the dependency array, otherwise it can cause bugs. Have a look at it here. Hooks FAQ
Regarding the official documentation clean-up function of react with useEffect help should be called only once and before component will be destroyed.
So regarding this information I made a component and put some code for example let it be the next one
useEffect(() => {
console.log('didmount')
return () => {
console.log('will unmount2')
}
})
I expect to see a several didmount consoles, depending on how many times I'm going to changes props of it and only one "will mount2" before I change my route, so component will be destroyed,
but actual result is next
What I've just missed ?
You didn't understand or fully read the docs.
Additionally, if a component renders multiple times (as they typically do), the previous effect is cleaned up before executing the next effect. In our example, this means a new subscription is created on every update.
Changing the props causes the component to rerender. Which in turn causes the clean-up function to be executed.
For your example this means that you should see serveral didmount and will unmount2 in the console depending on how many times the component did rerender.
hello Artem in your function you are missing the second param wich is the dependency array, without this array will cause an infinite loop ill suggest to try this
// this will run exactly when the unmount component, not when the effects finishing running
useEffect(() => {
return () => {
console.log('unmount')
}
}, [])
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.