
How to store your state data?
A more convenient way using a map

We usually store data information as array in the state. It does not matter if you are using Vue, React or Angular.
You usually do something like:
state.users = [//array of users]
Imagine now you want to remove a user.
state.users.splice(state.users.indexOf(user),1)
You need to walk the entire list to find out the index of the user and then remove it. That is inconvenient.
A better approach is to store the data in a JSON-like form, so with a map.
state.users = createAMapFrom([//array of users])
Imagine that createAMapFrom creates a map using a specific key, maybe the user_id. Now we can quickly perform CRUD operation by just access the map in constant time
delete state.users[userToRemove.user_id]
Much better. But a problem emerged, where is the original array? To get it back we need to fetch all the values from the users’ map by doing something like
Object.keys(state.users).map(key => state.users[key])
or we can use Object.values. But we have a problem: fetching all the values every time even if we do not change the state may be costing.
Resource class
First of all. You can find the code here:
https://github.com/FrancescoSaverioZuppichini/Resource
My solution is to provide a class, called Resource that implements this idea in a more general way. It updates the inner data array only when the map is changed. Let’s take a quick look:
class Resource {
constructor() {
this.cache = new Observer(this.listener.bind(this))
this.cache.listener()
}listener() {
this.data = Object.keys(this.cache._state).map(key => this.cache._state[key])
}fromArray(array, key = 'id') {
array.forEach(el => this.add(el, key))
}fromObject(object) {
this.cache.override(Object.assign(this.cache._state, object))
}removeMultiple(arrayOfIds) {
arrayOfIds.forEach(id => this.remove(id))
}update(newEl, key) {
var oldEl = this.get(key)
Object.assign(oldEl, newEl)
}remove(id) {
this.cache.remove(id)
}add(el, key = 'id') {
// do not override the object pointer
if (this.get(el[key]))
this.update(el, el[key])
else {
this.cache.add(el, key)
}
}get(key) {
return this.cache.get(key)
}
}
The resource class keep an inner cache that will automatically call the listener function every time something change. In our case it updates the data field that is the array with the actual data.
You can see that Resource exposes the classic CRUD methods: update, remove, add and get.
Also you can create and add multiple objects at the same time. For instance, imagine we receive an array of users after an API call.
var users = new Resource()
users.fromArray([//array with users that we fetched])
console.log(users.data) // [//array with users that we fetched]
Super easy!
The default key to store the data into the map is ‘id’, but we can also provide it as second parameter
var users = new Resource()
users.fromArray([//array with users that we fetched],'email')
Now the users are stored by email inside the map.
We can always get the original array by calling the data field
users.data = [//original array]
Remember that the function that pulls each value from the map is called only when we modify!
We can of course do other basic CRUD operation:
users.remove(email@email.com) // will remove user with that email
users.get(email@mail.com) // will get an user with that email
users.add({//user obj})
users.removeMultiple([emails...]) // remove multiple user at one time
users.update({//oldUser},{email:'new_email@email.com'}) //update only the new fiels
I have provided an example with Vue + Flue (a state managment library that I wrote) and Resource. You can find it here:
If you open the console you can see the classic redux-logger. If you want to know more about Flue: https://medium.com/@FrancescoZ/flue-another-flux-library-82ff43f70899
The interesting part is there in the code of the UserStore:
https://francescosaveriozuppichini.github.io/resource-users-wall-example/
You can see how easy is to create a resource from some real world data. We fist dispatch an action:
axios.get('https://randomuser.me/api/?results=12')
.then(data => ctx.dispatchAction('GET_USERS_SUCCESS', data))
And then
reduce(action) {
this.reduceMap(action, { GET_USERS_SUCCESS: this.onGetUsersSuccess })
}onGetUsersSuccess({data}) {
this.state.users.fromArray(data.results, 'email')
}
Of course this.state.users is a resource instance.
Installation
The package is available on npm:
You can install by
npm install resource-classimport {Resource} from 'resource-class'var something = new Resource()
Conclusion
We have seen how to create a better way to store information by combining a map in order to fast access the data and a smart way to fetch the original array back only when we need it.
I hope you like the article.It would be great to have some feedback from you guys.
Thank you for reading
Francesco Saverio