Tengo una clase simple, algo como esto:
class ReallyHugeClass { constructor() { this.counter = 0; } increment = () => { this.counter += 1 } }Si lo uso en el código de una manera sencilla, no mantendrá su estado. La clase se recreará cada vez que se renderice y no es reactiva en absoluto.
const Component = () => { const instance = new ReallyHugeClass(); return ( <button onClick={instance.increment}> {instance.counter} </button> ) }No se apresure a decir: ¡no necesita la clase! Escribe esto:
const Component = () => { const [counter, setCounter] = useState(0); return ( <button onClick={() => { setCounter(value => value + 1) }}> {counter} </button> ) } Usé el ejemplo de clase ridículamente pequeña, pero el real es complicado. Muy complicado. No puedo simplemente dividirlo en el conjunto de llamadas de useState .
Sigamos adelante. Puedo ajustar la instancia a useRef para guardar su valor.
const Component = () => { const instance = useRef(new ReallyHugeClass()); return ( <button onClick={instance.current.increment}> {instance.current.counter} </button> ) }El valor se guarda, pero aún no es reactivo. De alguna manera puedo obligar al componente a volver a renderizar pasando la devolución de llamada correspondiente a la clase, pero se ve incómodo.
¿Cuál es el patrón correcto para resolver tal tarea en React? Parece que es una situación bastante probable.
Una solución sería utilizar useRef y forzar la representación con un estado de uso. Aquí un ejemplo:
const { useRef, useState } = React; class ReallyHugeClass { constructor() { this.counter = 0; } increment() { this.counter += 1; console.log(this.counter); } } function App() { const instance = useRef(new ReallyHugeClass()); const [forceRender, setForceRender] = useState(true); return ( <button onClick={() => { instance.current.increment(); setForceRender(!forceRender); }} > {instance.current.counter} </button> ); } const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <> <App /> <App /> </> ); <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script> <script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js" ></script> <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" ></script> <div id="root"></div>