I have the next situation in my react application. I have a state that is set from another component: const [test, setTest] = useState();, when this state is set it create some renders like:
first render test = first value
second render test = first value
last render test = the expected value.
What i want to do is to read only the last value of test inside useEffect hook like:
useEffect(() => {
// here i need to avoid the previous values of test and to display only the last
console.log(test);
}, [test]);
Question: How inside useEffect to avoid the previous values of test and to display only the last?
You can initialise a counter variable outside the uesEffect, and increment it everytime the useEffect runs. Once you the desired condition is met(eg, if(counter===2)), then run console.log.
so
let counter = 0;
useEffect(()=>{
counter++;
if(counter==2){
console.log(test)
}
},[test])
I don't think the best solution is to wait for the 3rd call of the useEffect. It already means that the first 2 renders are useless, not too optimized but ok...
On the other hand, why not just test the value where you need it?
if(!!test && test === expectedValue)
or in jsx / tsx :
{!!test && test === expectedValue && <></>}
Related
Hello I am learning about the useEffect hook in react, and I need some clarification. It is my understand that when we provide an empty dependency array that the code inside of the useEffect hook will only run once during the application's initial mount. I need some helping understand why the code below runs every time I refresh the page even though I provided an empty dependency array?
Thank you
const [numberOfVistors, setnumberOfVistors] = useState(() => {
localStorage.getItem("numberOfVistorsKey")
});
useEffect (() => {
let vistorCount = localStorage.getItem("numberOfVistorsKey")
if (vistorCount > 0)
{
vistorCount = Number(vistorCount) + 1
localStorage.setItem("numberOfVistorsKey", vistorCount)
setnumberOfVistors(vistorCount)
}
else
{
vistorCount = 1
setnumberOfVistors(vistorCount)
localStorage.setItem("numberOfVistorsKey", vistorCount)
}
}, [])
The context of an application does not persist across multiple pageloads or refreshes except through the few ways that allow for persistent data - such as local storage, cookies, and an API with a server somewhere. For the same reason, doing
let num = 5;
button.onclick = () => { num = 10; }
results in num still being 5 after you click and reload the page - because reloading the page starts the script from the very beginning again, on the new page.
If you want to keep track of whether that particular section of code has ever run before, use a flag in storage, eg:
useEffect(() => {
if (localStorage.hasVisitedBefore) return;
localStorage.hasVisitedBefore = 'yes';
// rest of effect hook
, []);
I need some helping understand why the code below runs every time I refresh the page even though I provided an empty dependency array?
Every time you refresh the page, the React component mounts for the first time.
useEffect(..., []) was supplied with an empty array as the dependencies argument. When configured in such a way, the useEffect() executes the callback just once, after initial mounting.
Try going through it !!
Whenever you refresh the page, React component will be re-rendered. Therefore useEffect() is called everytime.
In order to avoid it, you have to do like this.
const [numberOfVistors, setnumberOfVistors] = useState(() => {
localStorage.getItem("numberOfVistorsKey")
});
useEffect (() => {
let vistorCount = localStorage.getItem("numberOfVistorsKey")
if (vistorCount > 0)
{
vistorCount = Number(vistorCount) + 1
localStorage.setItem("numberOfVistorsKey", vistorCount)
setnumberOfVistors(vistorCount)
}
else
{
vistorCount = 1
setnumberOfVistors(vistorCount)
localStorage.setItem("numberOfVistorsKey", vistorCount)
}
}, [numberOfVistors])
This code will render your component whenever numberOfVistors are changed.
const [hanziList, sethanziList] = useState([]);
const [whetherFinish, setWhetherFinish] = useState()
const inspectCheckboxList = () => {
var i = 0;
while (i < hanziList.length) {
if (hanziList[i].memorize === 0) {
setWhetherFinish("notFinish")
break;
}
if (hanziList[i].memorize === 1) {
i = i + 1;
}
if (i === hanziList.length) {
setWhetherFinish("finish")
}
console.log(whetherFinish)
}
}
useEffect(() => { inspectCheckboxList(); }, [hanziList]);
hanziList is an object which came from MySQL, it is a list of Chinese voca. and on List, if I check checkbox then the value of memorize turns into 1. So, 0 means not memorized, 1 means memorized.
If I check all of the checkboxes, it means that I have memorized all of the voca on the list, so the className of a button changes from "notFinish" into "finish".
And to check whether it is "finish", I made a function inspectCheckboxList. Every time the value of memorize changes, by useEffect render the function.
I found that putting state inside useEffect is a way to solve the problem, but it is not possible for me.
I'm not sure why the infinite loop occurs, because by useEffect, the function only renders when the website first laods, and the value of memorize (in this case, hanziList`) changes.
I think this is because inside function concludes hanziList, and the second dependency is also hanziList.
Why does the infinite loop occur, even when I set second dependency, and how to solve this problem?
The below code correctly updates the count state, but when outputting the count value with console.log, it is showing very strange behavior if called from a function within an setInterval inside useEffect() hook.
You would expect to see an incremental number in the console.log but the output from the fetchTimelineItems() function is bizar. When the count is 1, the output alternates between 0 and 1. When the count is 2 or more it outputs all the numbers in random order.
See codesandbox link to reproduce this behavior.
The expected behavior is to see the correct count value in the fetchTimelineItems() function.
Thanks in advance for pointing me in the right direction to get this fixed.
const Example = ({ title }) => {
const [count, setCount] = useState(0);
const handleCount = () => {
setCount(count + 1);
console.log(count);
};
function fetchTimelineItems() {
console.log("count from within fetch function: " + count);
}
useEffect(() => {
setInterval(() => {
fetchTimelineItems();
}, 3000)
},[count]);
return (
<div>
<p>{title}</p>
<button onClick={handleCount}>Increase count</button>
</div>
);
};
https://codesandbox.io/s/count-update-s5z94?file=/src/index.js
The useEffect hooks, runs after mounting and updating (depending on your dependency array) of your functional component.
So, it keeps running whenever you update your count.
Now, once you update count for the first time, the useEffect will again run, thus creating a new Interval because of setInterval. This is why you have multiple output statement.
Now, finally, each Interval you create is creating what is called a closure inside it. Inside this closure there is the fetchTimelineItems function along the value of count at that point of time.
So, for every update of count you are creating new intervals like this.
Mount -> Closure with fetchTimelineItems and count = 0,Update count once -> Closure with fetchTimelineItems and count = 1,
Update count again -> Closure with fetchTimelineItems and count = 2,
This is why you have all the values printing in the console.
Why it is printing old values is because that's how closures work in javascript. They remember the values at the time of their creation.
react hook useState works like setState when updating the state.
That's mean asynchronously operation.
My project lean on updating state with some timeout of 2 sec.
If I have an array with 3 values and I want to make a loop and change the state every 2 sec, but the operation just take the last state like an asynchronous functions.
any idea?
for (let i=0; i<arr.length; i++) {
setTimeout(() => setStr(arr[i]), 2000);
}
To give some idea, you need a setInterval that uses next item in array every time it is invoked, something like this:
const current = 0;
const interval = setInterval(() => {
if (current === arr.length - 1) {
clearInterval(interval);
}
setStr(arr[current])
current++;
}, 2000);
Of course you need to run it as an effect, keep the variables in a ref, clear interval on unmount etc.
So I found the answer myself.
create a promise by instantiate the promise class.
then make a function with set timout and resolve the setState function.
call it from from other function and loop in over in.
If the title wasn't clear, I want to run some code when the component mounts, and some other code when a particular variable changes. I could add the variable in the [], but the problem is, I want some code to run only once, and not when the variable changes.
FYI: The variable is a window property
Thanks in advance!
Have two separate effects to handle each case.
Case 1: when the component mounts
useEffect(
() => {
// do something
},
[] // no dependency: run once
)
Case 2: when the variable changes
useEffect(
() => {
if (variable) {
// do something
}
},
[variable] // with dependency: run every time variable changes
)