if i used setInterval(line 15) without useEffect than it gives result 2^n-1(0,1,3,7,15,31,63...) instead of(0,1,2,3,4,..) . so i have some question
1)why i am getting that output when I directly called setInterval without useeffect 2)is there any way if I change setCount(line 9) and its gives correct output by use setInterval directly without useEffect(as I did)
3) if the use of setInterval is not possible without useEffcet than why it is not possible?
if i put setInterval in useEffect and render initially once( line 12,13,14) than it gives correct output.....
but I do not get the correct output when I use directly setInterval. what is diff bet them?
in both cases, I call setInterval once but the output is diff.
import React, {useEffect, useState } from 'react'
export default function IncorrectDependency() {
const [count,setCount]=useState(0)
const inc=()=>{
// console.log(count)
setCount(preVal=>preVal+1)
// setCount(count+1)
}
// useEffect(()=>{
// setInterval(inc,1000)},[]
// )
setInterval(inc,1000)
return (
<div>
<h1>{count}</h1>
</div>
)
}
When we do a set state, functional components will re-execute from top to bottom, how ever when we use useState, useCallbacks etc.. they will not re-initialize as variables, functions,
So in this case, setInterval will re-initialize on each and every setCount, because of the state got changed,
step by step
in the 1st second there will be one setInterval, and call setCount and component is ready to rerender
when re-redering, start executing functional component from top-to-bottom it sees setInterval again and it will trigger it, so now we have two setIntervals
so on it will add multiple setIntervals on each second, because we don't clear it, so you should see the number printed in the browser will not take a second, but less than a second when time goes by.
You can achieve the expected result without useEffect by clearing the previous interval on each re-render which is happen due to setCount
create a variable to hold the set interval, code
const interval = null;
//this should be declare out side the component,
//because if we declare it inside the component it will redeclare,
//and the reference to the previous setInterval will be lost in that case no-way to clear the setInterval.
export default function IncorrectDependency() {
....
if (interval) {
clearInterval(interval);
}
interval = setInterval(inc, 1000);
....
}
alternatively react has a hook which can hold the same variables without re-initializing on each renderings, check it out useRef
here is a code-demo
const intvl = useRef(null);
....
if (intvl?.current) {
clearInterval(intvl.current);
}
intvl.current = setInterval(inc, 1000);
.....
when you directly use setInterval what is happening as this is a function it will be called on state change so again a setInterval will be triggered and so on which actually give you the incorrect result, so you shouldn't use setInterval without use effect, also on unmount you should clearthe interval
Dan Abramov explains why this isn't a good idea:
"it’s not the idiomatic way to do it. eg it won’t work correctly if you have multiple instances of the same component. it breaks the rules — it does a side effect (setInterval) during rendering, which the page said you should not do :) once you break the rules, all bets are off"
https://twitter.com/dan_abramov/status/1577395235340095501?s=20&t=nNnYyjLvHs5by_dqF5l8zg
Related
Edit
Okay my bad, I guess the response is simple. It's a javascript matter. I am not passing a function to useState but actually I am re-executing it everytime. So yeah what I am seeing is normal.
I will let my question here anyway.
A codesandbox example will follow.
Question :
Could someone please explain to me why React re-execute the inner function in a useState (or useRef) despite the returned value will be completely ignored (except the first execution) ? Is this the expected behaviour of useState ?
Explanation :
I mean why I see this console log here after each re-render
const [count, setCount] = React.useState((function(){
console.log('Executed !');
return 5;
})())
I used to think this inner function (or values) is the equivalent of a Class Parameters or Component Constructor but it's obviously not the case.
Returning a simple value does the job of course : React.useState(5).
But imagine using a third-party class which works with static values at some point or you simply don't want to re-execute it over and over again after each re-render ?
A possible fix for this is using useEffect with an empty dependency. (The equivalent of componentDidMount)
But I am asking about the purpose of this execution ?
let count = 0; // Number of component renders
const Test = (props) => {
const [innerCount, setCount] = React.useState((function(){})(
console.log('Executed !'); // Executed after each re-render
count = count + 1;
return count;
));
return innerCount; // Will be always 1 !
}
Why the anonymous function will be executed if the returned value is always 1 = the first returned value ?! What's the purpose of this execution ?
This is an example on CodeSandbox
This isn't React-specific behaviour, it's just what Javascript - and almost every mainstream programming language - does.
Before calling a function, it has to evaluate its arguments in order to know what to pass in.
Yes, it so happens that React.useState is a function that often "ignores" its argument. (I'm not familiar with precisely how it's implemented - clearly it isn't as simple as that as it does need to know when it's the first time it's being executed in that particular component instance, because in that case that value is used and returned.) But the browser Javascript engine can't possibly know that or optimise it away.
To prove this isn't anything to do with React, I can do this:
function ignoresArgument(x) {
return 1;
}
for (let i = 0; i < 20; i++) {
ignoresArgument(
(function(y) {
console.log("I don't need to be executed, but Javascript doesn't know that so I will be executed 20 times");
return 2;
})(1)
);
}
In addition to all the other answers, I think this link addresses your issue where you only want to call the function in useState once.
You can change this line in the sandbox:
const [dependency] = React.useState(() => new ThirdParty());
and the third party counter would stop incrementing.
I think your understanding of useEffect is somewhat wrong. It is not just used for componentDidMount. What you are describing is a symptom of a side-effect that cases state changes in the components. Here is the explanation of useEffect from the official documentation.
If you’re familiar with React class lifecycle methods, you can think
of useEffect Hook as componentDidMount, componentDidUpdate, and
componentWillUnmount combined.
useEffect would be ideal for this situation if you want to avoid re-renders that is not required.
React memo might not be needed for your solution either.
The following is a simple react component:
import React from "react";
import { useState } from "react";
export default function Comp() {
let [count, setCount] = useState(1);
function countUp(){
setCount(count + 1);
}
setInterval(countUp, 1000);
return <h2>{count}</h2>
}
I expected the counter to go up every second
But for some reason, after ten - twenty seconds something starts to go wrong
See here:
https://stackblitz.com/edit/react-az7qgn?file=src/comp.jsx
Can anyone explain this?
You should use useEffect hook to set up that properly. I can provide an example.
import React, { useState, useEffect } from "react";
export default function Comp() {
const [count, setCount] = useState(1);
useEffect(() => {
const interval = setInterval(() => {
setCount(state => state + 1);
}, 1000);
return () => clearInterval(interval);
}, []);
return <h2>{count}</h2>
}
A couple of notes.
In general, you would prefer const over let, but this is mandatory when destructuring things coming from React.
I suggest to read Using the Effect Hook on React docs to more information about useEffect.
Basically, useEffect allows you to achieve similar results to componentDidMount and componentDidUpdate lifecycle methods for class components. Also, in this specific case, by returning a function in useEffect callback, we make sure to clear the scheduled callback when it's time to clean up, which means after each run. This actually avoids the mess of stacking many setInterval on top of each other.
Also, when you setCount it's preferable to get the previous state by using the callback form, because that will be always up-to-date.
When calling setInterval(), it returns an interval id. Your code is not saving the variable, and thus you cannot reset it. On smaller iterations, you will not see the changes for every iteration. But, as the number of times that setInterval() is called increases from 0 to N, more timers are being initiated, and you will rapidly see flashes of numbers as they increase, because every interval is changing the state of count.
In other words, you are creating more and more timers as time goes on, rather than creating timers for one-time use. You will need to call clearInterval(timer_id_goes_here) to clear the timer. See code examples in the link below.
https://www.w3schools.com/jsref/met_win_setinterval.asp
I'm new to react-native and I was wondering about the running flow of it. For example:
import React, { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<View>
.
.
.
.
</View>
);
}
does the part before the return statement runs once or every render?
or every time the component gets called?
what if this component got called inside a return statement of another component , does the state gets reset every render?
The part outside return will be executed only one time when we call the component.
If you want your code to run multiple times you can you useEffect that will run your code according to your need as you pass dependency variable in a array as second argument of useEffect. Yes as how many times you call any component is will create new states for that component, It will now affect previous state of that component if called. I think I covered you doubt is my short answer, If I left something Please let me know.
There are two different types of components:
Stateful (class) components and Stateless (function) components (the one you are using).
The class components will only execute the render() method every time state changes and function will execute all the code every time you change the state inside it.
You have to know wich is best for your use cases
I've converted all my class components to functional components. Now, when I change some code in the IDE during development my page goes in an infinite loop. I'm almost certain one on my useEffect() hooks has an mistake (or missing) conditional firing variable. But it would take some time to figure out which one.
So, my question is. Is there an easy way to figure out which one is broken that would cause the loop. And would it also loop after build, or only in development?
As requested a code example. It is very basic since I have about 20 using this principle.
import React, {useEffect} from "react";
const Layout = ({ data }) => {
useEffect(() => {
// some jQuery stuff
}, [data.myConditionalVar])
return (
<div>
// my stuff
</div>
)
}
useEffect gets called every time the second argument you pass is changed. In your case it is data.myConditionalVar.
So, I am assuming inside useEffect you are updating the data using jquery which forces React to call the useEffect again resulting in a never-ending loop.
I have the following code(react 16.8.6, react-dom: 16.8.6):
import React, { useState, useEffect, } from 'react';
import ReactDOM from 'react-dom';
function App(props) {
const [counter, setCounter] = useState(0);
console.log(1)
useEffect(() => {
console.log(2)
setTimeout(() => {
console.log(3)
setCounter(1);
}, 10000)
});
return counter;
}
ReactDOM.render(<App />, document.getElementById('root'));
When App component is rendered first time, it prints:
1
2
After 10 seconds it prints:
3, 1, 2
Everything that was before is understandable for me, but now after 10 seconds it prints
3, 1
i.e. function that is passed to useEffect isn't called. I assume it's related to state somehow (as it doesn't change, if it changes useEffect works fine). Could you explain this behaviour?
According to the docs
Does useEffect run after every render? Yes! By default, it runs both after the first render and after every update. (We will later talk about how to customize this.) Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.
An update never occurred after you call setCounter a second time, because 1 === 1 is always the same.
If you actually increment your counter by one every time you'll get your desired effect.
function App(props) {
const [counter, setCounter] = useState(0);
console.log(1)
useEffect(() => {
console.log(2)
setTimeout(() => {
console.log(3)
setCounter(counter + 1);
}, 10000)
});
return counter;
}
Live example
From the React docs:
If you update a State Hook to the same value as the current state, React will bail out without rendering the children or firing effects. (React uses the Object.is comparison algorithm.)
Note that React may still need to render that specific component again before bailing out. That shouldn’t be a concern because React won’t unnecessarily go “deeper” into the tree. If you’re doing expensive calculations while rendering, you can optimize them with useMemo.
Thats exactly what happens here: The component rerenders, but as the current state is 1 and you set it to 1 after the second timeout React "bails out" and thus does not retrigger the effect.
Sidenote: The effect should really only depend on counter, not on an update in general.
Every time your component renders, useEffect will be called because you do not provide an array as a second parameter.
If you read the docs, you will read, that if you do not provide a second parameter, it will be called every time.
If you provide an empty array, it will only be called on the first render.
If you provide a variable into the array, it will only be executed if that variable changes.
3,1 will be called because after the 2 is called, the timeout is set again and will print 3. After updating the state with setCounter(1);, the component rerenders and 1 will be called again.
Hope this helps. Happy coding.