Count value Sate not getting updating - React JS - reactjs

When load more is clicked, count value is not getting updated only on the second click it is getting updated.
Expectation is on load more button click value should be passed as 2, but now it is sending as 1.
what I'm doing wrong here. Please guide
Below is the sample code.
const [count, setCount] = useState(1);
fetchData() {
.....
}
loadMore() {
....
fetchData()
}
Render HTML Method:
<button onClick={ () => {
setCount(count + 1);
loadMore();
}}

use this -
const handleOnClick = () => {
setCount((count) => count + 1);
loadMore();
}}
<button onClick={handleOnClick}>Click me</button>

You may check the answer here in this react documentation.
I’ve updated the state, but logging gives me the old value.

Related

understanding useCallback and setState

I have some simple lines of code to test the useCallback and setState hook. First I try to write like this:
const App = () => {
const [count, setCount] = useState(0)
const increase = useCallback(() => {
setCount(count + 1) // Passing value to change state
}, [])
console.log(count)
return (
<div id="main">
<h1>Count: {count}</h1>
<button onClick={increase}>Increase</button>
</div>
)
}
export default App;
and then, passing the callback to setCount instead like this:
const App = () => {
const [count, setCount] = useState(0)
const increase = useCallback(() => {
setCount(count => count + 1) // Passing callback
}, [])
console.log(count)
return (
<div id="main">
<h1>Count: {count}</h1>
<button onClick={increase}>Increase</button>
</div>
)
}
export default App;
I know it is weird to use useCallback in this situation, just for some testing. The question is that why I pass the callback to setCount, the Increase button work correctly, whereas the first one will just re-render on the first click
In the first case useCallback closes over the count and at that time count was 0 so It always changes value to 1
So to make it working you should add count dependency to useCallback as:
CODESANDBOX LINK
const increase = useCallback(() => {
setCount(count + 1); // Passing value to change state
}, [count]);
In the second case it is not taking the value from the closure instead it is taking the current value and adds one to it and set the value.
ADDITIONAL INFO
You can use react-hooks/exhaustive-deps eslint rule to know which dependency is missing and tell you how to correct it.
useCallback() hasn't been used correctly here: the dependencies array should have count in it. So:
const increase = useCallback(() => {
setCount(count + 1) // Passing value to change state
}, [count])
With an empty array as a dependency, it will be memoized for the lifetime of the component and will not update the current count value.
Source: https://reactjs.org/docs/hooks-reference.html#usecallback
Code sandbox
On a side note, using useCallback() is "overoptimizing" in this case, we are complicating the code while the performance impact to the end users is not noticeable. I would only use it to prevent rerenders in complex views e.g. in a huge list.

useDispatch callback uses stale state when triggered multiple times [duplicate]

Normally when we need to update a state in a functional component, we do something like this:
function Example() {
const [count, setCount] = React.useState(0);
return (<div><p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>);
}
When and why will we ever need to use the functional update form?
function Example() {
const [count, setCount] = React.useState(0);
return (<div><p>You clicked {count} times</p>
<button onClick={() => setCount(c=>c + 1)}>
Click me
</button>
</div>);
}
Use the function form when the setter may close over an old state value.
For example, if an async request is initiated, and you want to update state after that's done, the request that was made will have scope of the state as it was at the beginning of the request, which may not be the same as the most up-to-date render state.
You may also need to use the function form if the same state value was just updated, eg
setValue(value + 1);
// complicated logic here
if (someCondition) {
setValue(value => value + 1);
}
because the second call of setValue closes over an old value.
State Updates May Be Asynchronous:
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
useState is the same as setState in this condition.
You can see the different when call set state twice:
<button
onClick={() => {
setCount(count + 1);
setCount(count + 1);
}}
></button>;
<button
onClick={() => {
setCount(c => (c + 1));
setCount(c => (c + 1));
}}
></button>;
There are other use cases too. For example, when you call useState inside an effect. If new state is dependent on old state, this might cause an infinite loop.
useEffect(() => {
setCounter(counter + 1);
}, [counter]);
You can avoid this by using functional updates:
useEffect(() => {
setCounter(old => old + 1);
}, []);
According to this: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
The the functional update form make sure that the previous state that you take reference from is the latest / finalized version when there might be multiple setState hook (which is asynchronous) called (for example, if the user spam click on the button)
And also due to its async nature, the state will not be updated right away within a function, for e.g:
func() {
console.log(counter) // counter = 1
setCounter(counter => counter + 1) // counter = 1
console.log(counter) // counter = 1
}
The functional update form also allows the update function to be passed to its children while still having access to the parent’s state.
function MyButton(props){
// return <button onClick={()=>props.onClick(count+1)}>+1</button>; // error as count is not exposed here
return <button onClick={()=>props.onClick(n=>(n+1))}>+1</button>;
}
function Example() {
const [count, setCount] = React.useState(0);
return (<div><p>Counter: {count}</p>
<MyButton onClick={setCount}/>
</div>);
}
ReactDOM.render(<Example/>,document.querySelector("div"));

ReactJS: How to detect value changed into another componet and compare with previous?

I am doing a simple exercise to clear my hook concept.
In my current scenario, I need to call the API only if i click on the Call API button and that time the page number should be the current value of Click Me button value.
But currently, on every Click Me button click the API is called.
I have tried with this solution, but can not get the result.
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
console.log(ref.current);
return ref.current;
};
export default function GetAPIData(props) {
const [chars, setChars] = useState([]);
const prevCount = usePrevious(props.id);
console.log(
"Previous value is: " + prevCount,
"Current value is: " + props.id
);
useEffect(() => {
axios
.get(`https://rickandmortyapi.com/api/character/?page=${props.id}`)
.then((res) => {
setChars(res.data.results);
})
.catch((err) => {
alert(err.message);
});
}, [prevCount, props.id]);
return (
<div className="container">
<h3>
<span>Achtung,</span> ein Trend geht um
</h3>
</div>
);
}
Here is the working Sandbox URL for reference.
You are updating the state every time when you click on the button by that the page was re-render and useEffect() is calling the API.
Try to create a new state which only changes when you click on the Call API. By that, your component does not re-render on the change of count.
Might it help You

When pressing button, setState works only on second press

I'm trying to increment page state on a button press. When I press the button, state is 1. It works only after next press.
const { state, fetchMissions } = useContext(MissionContext);
const [page, setPage] = useState(1);
const loadMoreData = () => {
setPage(page + 1);
if (state.missions && page !== 1) {
fetchMissions(page);
}
};
<Button title="load more data" onPress={loadMoreData}></Button>
Also using navigationEvents
<NavigationEvents onWillFocus={() => fetchMissions(page)} />
Am I missing something? Thank you.
setState is asynchronous. The value does not have to be updated on the next line.
Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState
Source: reactjs.org, Why is setState giving me the wrong value?
I would recommend to use useEffect.
const { state, fetchMissions } = useContext(MissionContext);
const [page, setPage] = useState(1);
const loadMoreData = () => {
setPage(page + 1);
};
useEffect(()=>{
// this is called only if the variable `page` changes
(state.missions && page !== 1) && fetchMissions(page);
}, [page]);
By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.
Source: reactjs.org, useEffect
So I've put [page] as second argument to tell React when to call this part of code.

Handling out of date state in functional components

I am running into issues setting a state created with the 'useState' hook from within async functions.
I've created a codepen to demonstrate: https://codepen.io/james-ohalloran/pen/ZdNwWQ
const Counter = () => {
const [count, setCount] = useState(0);
const increase = () => {
setTimeout(() => {
setCount(count + 1);
},1000);
}
const decrease = () => {
setTimeout(() => {
setCount(count - 1);
},1000)
};
return (
<div className="wrapper">
<button onClick={decrease}>-</button>
<span className="count">{count}</span>
<button onClick={increase}>+</button>
</div>
);
};
In the above example, if you click 'increase' followed by 'decrease'..you will end up with -1 (I would expect it to be 0).
If this was a React class instead of a functional component, I would assume the solution would be to use bind(this) on the function, but I didn't expect this to be an issue with arrow functions.
It is because of using setTimeout
Let's assume that you've called the increase() 10 times in a second.
count will be always 0. Because the state is updated after a second, every increment() called in a second will have an unupdated count.
So every increment() will call setCount(0 + 1);.
So no matter how many times you call in a second, the count is always 1.
Ah, I found a solution. I didn't realize I'm able to reference the previousState from the useState setter function: https://reactjs.org/docs/hooks-reference.html#functional-updates

Resources