I am learning hooks. I am trying to update state but it's not reflecting immediately.
Here is my code.
const Test = (props) => {
const [score , setScore] = useState(0);
const [count , setCount] = useState(0);
const [total, setTotal] = useState(0);
const playerBallClick= ()=> {
setCount(count+1);
setScore(Math.floor(Math.random() * 10));
setTotal(total + score);
}
return (
<div>
<button onClick={playerBallClick}>Ball</button>
{/* <p>Total score is - {totalscore}</p> */}
</div>
)
}
How can I update Total immediately onclick on Ball button.
You can use useEffect hook like so,
useEffect(() => {
setTotal(total + score);
}, [count]);
So everytime count state changes, this hook will be called updating your total state.
Score is a stale closure try the following instead:
const playerBallClick = () => {
setCount(count + 1);
const newScore = Math.floor(Math.random() * 10);
setScore(newScore);
setTotal(total => total + newScore);
};
Related
I want to create 2 variables that depend on state. What i want to achieve is self-explanatory in my code below.
const evenodd = () => {
const [counter, setCounter] = useState(0)
const handleClick = () => {
setCounter(counter +1)
}
// initiate with var, let, const, or useRef?
(?) even = counter*2
(?) odd = counter*2+1
return (
<div>
counter: {counter}
even: {even}
odd: {odd}
<button onClick={handleClick}>add</button>
</div>
)
}
export default evenodd
between var, let, const, or useRef which one should i pick for my 2 variables that depend on state? and for what reason?
Try to use useMemo hooks
const evenodd = () => {
const [counter, setCounter] = useState(0)
const handleClick = () => {
setCounter(counter +1)
}
const even = React.useMemo(()=> (counter * 2), [counter]);
const odd = React.useMemo(()=> (counter * 2 + 1), [counter]);
return (
<div>
counter: {counter}
even: {even}
odd: {odd}
<button onClick={handleClick}>add</button>
</div>
)
}
I have a simple increment app where you hit a button and the count increments by 1.
My question is: how do I update the state properly?
Here are the two ways I am wondering between and of course if there is any other "better" option please do tell me.
import React, {useState} from "react"
const App = () => {
const [count, setCount] = useState(0)
const increment = () => {
setCount(prevCount => prevCount + 1)
}
return (
<div>
<h1>The count is {count}</h1>
<button onClick={increment}>Add 1</button>
</div>
)
}
export default App
or
import React, {useState} from "react"
const App = () => {
const [count, setCount] = useState(0)
const increment = () => {
setCount(count + 1)
}
return (
<div>
<h1>The count is {count}</h1>
<button onClick={increment}>Add 1</button>
</div>
)
}
export default App
Could you tell me which one is the best way of updating the state and why? Thank you!
Imagine a case as follows, and you'll mean the difference:
const increment = () => {
setCount(prevCount => prevCount + 1)
setCount(prevCount => prevCount + 1)
}
Or:
const increment = () => {
setCount(count + 1)
setCount(count + 1)
}
Not the same behavior.
usually when use useCallback, it's better to use setCount(x=> x + 1);
const onIncr = React.useCallback(()=> {
setCount(x=> x + 1)
}, [])
vs
const onIncr = React.useCallback(()=> {
setCount(count + 1)
}, [count])
tips , this example can transform into
const [count, increment] = React.useReducer((x)=> x + 1, 0);
return <button onClick={increment}>{count}</button>
This technique is usually used in toggle value
const [isOpen, toggle] = React.useReducer(x=> !x, false);
return (
<>
<button onClick={toggle}>open dialog</button>
<Dialog open={isOpen} onClose={toggle}></Dialog>
<>
)
I am trying to update (increment) a React state (counter) every setInterval function but it is don't work as expected. For the first 5 or so seconds, the count is rendered with consistent increments but after that it increases randomly and doesn't stop.
export default function App() {
const [count, setCount] = useState(0);
setInterval(() => setCount((oldCount) => oldCount + 1), 1000);
return (<>
<div>{count}</div>
</>);
};
How can I achieve this and the same for time intervals less than a second ( 100 or 10ms ) ?
You need to run the interval in useEffect there are plenty of such examples:
export default function App() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => setCount((oldCount) => oldCount + 1), 1000);
return () => {
clearInterval(id);
};
}, []);
return (
<>
<div>{count}</div>
</>
);
}
Why is it that the correct count value can be obtained in setinterval after the first click, and then the transformation does not occur again?
import React, { useEffect, useState } from 'react';
const Demo1 = () => {
let [count, setCount] = useState(1);
const onCountClick = () => {
count += 1;
setCount(count);
};
useEffect(() => {
setInterval(() => {
console.log(count);
}, 1000);
}, []);
console.log(count);
return <button onClick={() => onCountClick()}>test</button>;
};
You are directly modifying the state. Instead do this:
setCount(count++)
React doen't really handle setInterval that smoothly, you have to remember that when you put it in componentDidMount (useEffect with an empty dependencies' array), it builds its callback with the current values, then never updates.
Instead, put it inside componentDidUpdate (useEffect with relevant dependencies), so that it could have a chance to update. It boils down to actually clearing the old interval and building a new one.
const Demo1 = () => {
let [count, setCount] = useState(1);
let [intervalId, setIntervalId] = useState(null);
const onCountClick = () => {
count += 1;
setCount(count);
};
useEffect(() => {
setIntervalId(setInterval(() => {
console.log(count);
}, 1000));
}, []);
useEffect(() => {
clearInterval(intervalId);
setIntervalId(setInterval(() => {
console.log(count);
}, 1000));
}, [count]);
console.log(count);
return <button onClick={() => onCountClick()}>test</button>;
};
The first thing is that changing the value of state directly like count += 1 is a bad approach, instead use setCount(count + 1) and you cannot console.log any value in the return statement instead use {count} to display the value on the screen instead of console.
The following code will increment the value of count on every click instance
const [count, setCount] = useState(1);
const onCountClick = () => {
// count += 1;
setCount(count + 1);
};
useEffect(() => {
setInterval(() => {
console.log(count);
}, 1000);
});
return (
<div className="App">
<button onClick={() => onCountClick()}>test</button>;
</div>
);
function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count; });
const prevCount = prevCountRef.current;
return <h1>Now: {count}, before: {prevCount}</h1>;
}
In the above snippet from the React Hoks FAQS useRef is used for saving the count. However whenever render is called, will not the prevCount be set to the current count as the useEffect will be called on each render, so how are count and prevCount different ?
That's because the useEffect callback function will be called after the rendering time of Counter Component has finished. and that's why prevCount is always one tick behind.
one point to consider is the change in in a React ref won't cause a rerender in React Component only change in state and props will cause a rerender.
see the working example here: https://codesandbox.io/s/lucid-butterfly-y66tc?file=/src/App.js
export default function Counter() {
const [count, setCount] = useState(0);
const prevCountRef = useRef();
useEffect(() => {
prevCountRef.current = count;
console.log('me socond')
});
console.log('me first')
const prevCount = prevCountRef.current;
return (
<h1>
Now: {count}, before: {prevCount}
<div onClick={() => setCount((v) => v + 1)}>click me</div>
</h1>
);
}
you see me first then me second in the console