React state shouldn't be mutated directly. But, what if the state is an instance of a class that shall be mutable with its own methods. Is there another way than having to deep-clone and re-instantiate the object with new parameters?
In General: What is the react way for a class object created in parent components to be used in subcomponents while maintaining its properties in the parent state (which is passed down via props/context)?
Sample class
class Car{
constructor(data){
this.data = data
}
changeColor = (newcolor) => this.data.color = newcolor
}
Sample Parent Component
const App = ({data}) => {
const [car, setCar] = useState(new Car(data))
return (
<div>
<CarViewer car={car} />
</div>
);
};
Sample Sub componeent
const CarViewer = ({car}) => {
return (
<div>
The color is: {car.data.color}
<button onClick={()=>car.changeColor("blue")}>Change color to blue </button>
</div>
);
};
The state is mutable in react components. To make the React applications interactive we almost use state in every react component. State is initialized with some value and based on user interaction with the application we update the state of the component at some point of time using the setState method.
State can hold any kind of JavaScript value, including objects. But you shouldn't change objects that you hold in the React state directly. Instead, when you want to update an object, you need to create a new one (or make a copy of an existing one), and then set the state to use that copy.
Props are immutable. State is mutable. 3. Props allow you to pass data from one component to other components as an argument.
I think what you need to do is change your mental model of storing a class inside a react state and try different model like this which is more react way:
const CarViewer = ({ carData, changeColor }) => {
return (
<div>
The color is: {carData.color}
<button onClick={() => changeColor("blue")}>Change color to blue</button>
</div>
);
};
const App = ({ data }) => {
const [carData, setCarData] = useState(data);
const changeColor = (newcolor) =>
setCarData((data) => ({ ...data, color: newcolor }));
return (
<div>
<CarViewer carData={carData} changeColor={changeColor} />
</div>
);
};
EDIT: based on your comment, I think what you need is a custom hook like this:
const App = ({ data }) => {
const { carData, changeColor } = useCar(data);
return (
<div>
<CarViewer carData={carData} changeColor={changeColor} />
</div>
);
};
function useCar(defaultData = {}) {
const [carData, setCarData] = useState(defaultData);
const changeColor = (newcolor) =>
setCarData((data) => ({ ...data, color: newcolor }));
return {
carData,
changeColor,
//... your other methods
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With