In easy-peasy, the useStoreState()
hook does not cause a re-render when we use the hook to access the store's field that stores an ES6 class instance. For example:
store.js
import { action, createStore } from "easy-peasy";
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const store = createStore({
person: new Person("Tom", "20"), // <-- Stores ES6 class instance
updatePersonName: action((state, payload) => {
state.person.name = payload;
})
});
export default store;
app.jsx
:
import "./styles.css";
import { useStoreActions, useStoreState } from "easy-peasy";
import React from "react";
export default function App() {
const person = useStoreState((state) => state.person);
return (
<div className="App">
<p>{JSON.stringify(person)}</p>
<EditPerson />
</div>
);
}
function EditPerson() {
const person = useStoreState((state) => state.person);
const updatePersonName = useStoreActions(
(actions) => actions.updatePersonName
);
return (
<input
value={person.name}
onChange={(e) => updatePersonName(e.target.value)}
/>
);
}
If we try to type in the input box, even though the updatePersonName
action is successfully dispatched (see the screenshot below), the value of the input box remains unchanged. The person
store state is not successfully updated and the useStoreState()
hook does not cause a re-render.
easy-peasy uses the Immer library to convert the mutations into the equivalent immutable updates against the state. However, Immer does not implicitly support custom ES6 classes.
According to the Immer's docs:
Plain objects (objects without a prototype), arrays, Maps and Sets are always drafted by Immer. Every other object must use the
immerable
symbol to mark itself as compatible with Immer
We can make our custom ES6 class compatible with Immer according to the docs:
store.js
import { action, createStore } from "easy-peasy";
import { immerable } from "immer"; // 👈 Add this (1)
class Person {
[immerable] = true; // 👈 Add this (2)
constructor(name, age) {
this.name = name;
this.age = age;
}
}
const store = createStore({
person: new Person("Tom", "20"),
updatePersonName: action((state, payload) => {
state.person.name = payload;
})
});
export default store;
Alternatively, we can convert the ES6 class into a plain JavaScript object if your ES6 class is simple enough:
store.js
import { action, createStore } from "easy-peasy";
const store = createStore({
// Use a plain JS object here 👇
person: {
name: "Tom",
age: "20",
},
updatePersonName: action((state, payload) => {
state.person.name = payload;
})
});
export default store;
easy-peasy-decorators
According to Easy Peasy docs, there's a community extension called easy-peasy-decorators
that provides the ability to generate stores via classes and decorators.
Unfortunately as at 2022 Nov, that library does not support Easy Peasy v4 or above (as hinted in this issue)