Do you want to request a feature or report a bug?
Bug
What is the current behavior?

If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem. Your bug will get fixed much faster if we can run your code and it doesn't have dependencies other than React. Paste the link to your JSFiddle (https://jsfiddle.net/Luktwrdm/) or CodeSandbox (https://codesandbox.io/s/new) example below:
See https://codesandbox.io/s/x31lqr09zq
What is the expected behavior?
If I click "count = count", it should not execute the function again
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
16.8.1. All browsers have the same issue
This may be unintuitive, but it's the expected behavior. See the docs: https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update. When you pass the same state value, React will call the component render function but it won't recurse to the children or run any effects.
If you're curious, this is because the updates are not processed until the render phase. For example if you write setCount(c => c) (a no-op state update), that state updater function is called during the render (we do this for consistency with useReducer, which does this in order to have access to the latest props). So there's no way to skip it. Similarly, if you were to call setCount(c => c + 1); setCount(count); with two updates that "cancel out", that isn't known until the render function is called.
Hey @sophiebits, Thanks for the explanation.
My following question is not about this issue. It is about the comparison algorithm for bailing out of a state update. Why not using the shallowEqual??

shallowEqual would be slower and not always what you want. However if you want shallowEqual behavior you can write it yourself using something like this:
function useStateWithShallowEqual(initialValue) {
let [value, setValue] = useState(initialValue);
let setWithShallowEqual = useCallback(function (newValue) {
setValue(val => {
if (typeof newValue === 'function') {
newValue = newValue(val);
}
if (shallowEqual(val, newValue)) {
return val;
} else {
return newValue;
}
});
}, []);
return [value, setWithShallowEqual];
}
Your answer is very important to me. Thanks!!
Hi @sophiebits, according to your answer, I've figured out that it only causes an extra re-render when value is changed then be updated with the same again.
Look at my sample code at https://codesandbox.io/s/aged-architecture-jhm6o
If you click the 2nd for the first time, no re-render trigger.
But if you click the 1st button, then click the 2nd button again, it causes an extra re-render.
And by the way, can you explain more why it need to re-render 1 time when it updates with the same value. Thanks in advance
Most helpful comment
This may be unintuitive, but it's the expected behavior. See the docs: https://reactjs.org/docs/hooks-reference.html#bailing-out-of-a-state-update. When you pass the same state value, React will call the component render function but it won't recurse to the children or run any effects.
If you're curious, this is because the updates are not processed until the render phase. For example if you write
setCount(c => c)(a no-op state update), that state updater function is called during the render (we do this for consistency with useReducer, which does this in order to have access to the latest props). So there's no way to skip it. Similarly, if you were to callsetCount(c => c + 1); setCount(count);with two updates that "cancel out", that isn't known until the render function is called.