Persisting the state
Finally, let's add a persistor to save the state to the local device disk. This is done during the store creation:
const store = createStore<State>({
initialState: State.initialState,
showUserException: userExceptionDialog,
persistor: persistor, // Here!
});
You can implement your own persistor, as long as it follows the abstract Persistor
interface,
provided by Async Redux.
Adding a persistor allows the user to reload the page, or close the browser and reopen it later, without losing the todo list.
ClassPersistor
Async Redux comes out of the box with the ClassPersistor
that implements the Persistor
interface. It serializes ES6 classes out of the box,
and it will persist the whole state of your application.
To use it, you must provide:
loadSerialized
: a function that returns the serialized state.saveSerialized
: a function that saves the serialized state.deleteSerialized
: a function that deletes the serialized state.classesToSerialize
: an array of all the custom classes that are part of your state.
In more detail, here's the ClassPersistor
constructor signature:
constructor(
// Returns the serialized state.
// It should return a Promise that resolves to the serialized state,
// or to null if the state is not yet persisted.
public loadSerialized: () => Promise<string | null>,
// Saves the given serialized state.
// It should return a Promise that resolves when the state is saved.
public saveSerialized: (serialized: string) => Promise<void>,
// Deletes the serialized state.
// It should return a Promise that resolves when the state is deleted.
public deleteSerialized: () => Promise<void>,
// List here all the custom classes that are part of your state, directly
// or indirectly. Note: You don't need to list native JavaScript classes.
public classesToSerialize: Array<ClassOrEnum>
)
For our Todo List app, this is the complete code:
- React
- React Native
let persistor = new ClassPersistor<State>(
// loadSerialized
async () => window.localStorage.getItem('state'),
// saveSerialized
async (serialized: string) => window.localStorage.setItem('state', serialized),
// deleteSerialized
async () => window.localStorage.clear(),
// classesToSerialize
[State, TodoList, TodoItem, Filter]
);
let persistor = new ClassPersistor<State>(
// loadSerialized
async () => await AsyncStorage.getItem('state'),
// saveSerialized
async (serialized) => await AsyncStorage.setItem('state', serialized),
// deleteSerialized
async () => await AsyncStorage.clear(),
// classesToSerialize
[State, TodoList, TodoItem, Filter]
);
Try it yourself
Add some items to the list, mark some of them as completed, and then reload the page. You should see the same items and their completed status.
Implementing your own serializer
If instead of using the provided ClassSerialized
you decide to implement your own
serializer, the following information may be useful for you.
In JavaScript there are two types of objects:
-
Plain (literal) objects - Instances of the
Object
class. Sometimes they are called literal objects, when created via the{}
notation. -
Class (constructor) objects - Instances of classes with own defined constructor, properties and methods. Usually you define them via class notation.
If you are loading a JSON from your backend, after you JSON.parse()
it,
you have a plain JavaScript object, not an instance of a class.
These packages will help you transform plain JavaScript objects into ES6 classes:
-
esserializer - This is the package used internally by the
ClassSerialized
. ESSerializer is a JavaScript serialization and deserialization utility, that helps you serialize classes to JSON, and then deserialize them back to class objects, with all properties and methods, recursively. No eval is used, which means it doesn't introduce any security risks. -
class-transformer - Allows you to transform plain JavaScript objects into some class object and versa, and also serialize and deserialize them.