Sintiéndome un poco tonto, he estado trabajando con React durante 2 años y siempre pensé que tenías que usar setState con una nueva copia del objeto para evitar la mutación del estado. Sin embargo, este ejemplo muta el estado y usa setState con la misma referencia de objeto sin ningún problema.
¿Por qué funciona esto?
const { useState } = React; const Counter = () => { const [countObject, setCountObject] = useState({count: 0}); const onClick = () => { countObject.count = countObject.count + 1; setCountObject(countObject); // mutated object, same reference } return ( <div> <p>You clicked {countObject.count} times</p> <button onClick={onClick}> Click me </button> </div> ) } ReactDOM.render(<Counter />, document.getElementById('app'))La inmutabilidad es muy importante, por supuesto, su código funciona, solo porque modificar el objeto original no afecta las funciones. Cambie el código compartiendo el valor inicial, luego haga clic en el botón arbitrario, encontrará algo mal.
const { useState } = React; const Counter = () => { const value = { count: 0 } const [countObject, setCountObject] = useState(value); const [countObject2, setCountObject2] = useState(value); const onClick = () => { countObject.count = countObject.count + 1; setCountObject(countObject); } const onClick2 = () => { countObject2.count = countObject2.count + 1; setCountObject(countObject2); } return ( <div> <p>You clicked {countObject.count} times</p> <button onClick={onClick}> Click me </button> <p>You2 clicked {countObject2.count} times</p> <button onClick={onClick2}> Click me2 </button> </div> ) } ReactDOM.render(<div><Counter /></div>, document.getElementById('app'))Su codepen está usando una versión alfa de React 16.7. Los ganchos se lanzaron en 16.8, por lo que para usarlos de manera confiable, debe usar una versión 16.8 o superior (en una versión no alfa de React):
Puede actualizar el número de versión de los dos enlaces anteriores haciendo clic en el ícono de ajustes en su codepen en el panel de JavaScript.
Observe que el siguiente fragmento usa 16.7.0-alpha.2 y tiene el mismo problema que estaba experimentando:
const { useState } = React; const Counter = () => { const [countObject, setCountObject] = useState({count: 0}); const onClick = () => { countObject.count = countObject.count + 1; setCountObject(countObject); } return ( <div> <p>You clicked {countObject.count} times</p> <button onClick={onClick}> Click me </button> </div> ) } ReactDOM.render(<Counter />, document.getElementById('app')) <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.7.0-alpha.2/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.7.0-alpha.2/umd/react-dom.production.min.js"></script> <div id="app"></div>Sin embargo, cambiar su versión a 16.8 o posterior soluciona el problema (al momento de escribir esto, estamos en v17.0.2):
const { useState } = React; const Counter = () => { const [countObject, setCountObject] = useState({count: 0}); const onClick = () => { countObject.count = countObject.count + 1; setCountObject(countObject); } return ( <div> <p>You clicked {countObject.count} times</p> <button onClick={onClick}> Click me </button> </div> ) } ReactDOM.render(<Counter />, document.getElementById('app')) <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.0/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.0/umd/react-dom.production.min.js"></script> <div id="app"></div>