'How to make a class value reactive in React
I have a simple class, something like this:
class ReallyHugeClass {
constructor() {
this.counter = 0;
}
increment = () => {
this.counter += 1
}
}
If I use it in the code in a straightforward way it won't keep its state. The class will be recreated every time on render and it's not reactive at all.
const Component = () => {
const instance = new ReallyHugeClass();
return (
<button onClick={instance.increment}>
{instance.counter}
</button>
)
}
Don't rush to say: you don't need the class! Write this:
const Component = () => {
const [counter, setCounter] = useState(0);
return (
<button onClick={() => { setCounter(value => value + 1) }}>
{counter}
</button>
)
}
I used the ridiculously small class example, but the real one is complicated. Very complicated. I can't just split it into the set of useState
calls.
Let's go forward. I can wrap the instance to useRef
to save its value.
const Component = () => {
const instance = useRef(new ReallyHugeClass());
return (
<button onClick={instance.current.increment}>
{instance.current.counter}
</button>
)
}
The value is saved, but it's still not reactive. I can somehow force the component to rerender by passing the corresponding callback to class, but it looks awkwardly.
What's the right pattern to solve such task in React? It looks that it's quite likely situation.
Solution 1:[1]
One solution would be to use useRef and force rendering with a useState. Here an example:
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>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 |