Like ember-data but for mobx and react
tl;dr
yarn add mobx mobx-react mobx-utils serializr striker-store
striker-strore is build on mobx stack, and our peer dependencies are mobx mobx-react mobx-utils serializr
.
This is a library that adds somethig like emaber-data
but for your React projects.
It also have an react-router
v4 integration to avoid boilerplate code.
Please check out demo
folder in source code. There is always up-to-date examples how to use library.
Model is your domain data storage.
Your model is a class
that have at least one @identifier
decorator. This is value will be used to track your model id.
We provide an helper base class for model called DomainModel
. It will add a lot of lifecycle indication to your model. (isReloading, isError, isSaving, isSaved, isUpdating, isDeleting)
All this indicators are boxed values
. To learn more: https://mobx.js.org/refguide/boxed.html
You can also define @serializable
decorator to properties you want to serialize and deserialize.
You can also define you custom methods, for some computed values for example.
All serialization & deserialization process covered by serializr
library from @mwestrate
More info: https://github.com/mobxjs/serializr
import { DomainModel } from 'striker-store'
import { serializable, identifier } from 'serializr';
class ExampleUserModel extends DomainModel {
@serializable(identifier()) id;
@serializable name;
}
Now you have all helpers you your model. And field id
will be used to track your model id.
Service is communication with API.
Your service is a class
that implements next methods:
- fetchAll
- fetchOne
- createItem
- updateItem
- deleteItem
We do not restrict that under the hood what type of transport you are use. All you need is to return an Promise
from those methods.
class ExampleUserService {
fetchAll() {
return fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json());
}
fetchOne(id) {
return fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
.then(response => response.json());
}
}
You are here because of the store
. Store
use your service
to fetch/update/delete/create you data. Than use you model
to serialize/deserialize you data and store it in memory.
Store have following methods to manipulate with your data:
- fetchAll
- fetchOne
- createItem
- updateItem
- deleteItem
As you can see naming of the methods is equavilent of your service
methods.
To add more control we provide hooks for you to override all aspects of store life.
It have next lifecycle (resolver) methods:
- storeDidFetchAll
- storeDidFetchOne
- storeDidCreateNew
- storeDidUpdate
- storeDidDelete
Those methods are used to map your backend response to what we need to save in store. Also, it can be used to show user notifications, etc.
By default all resolvers methods are next: (data) => data
But you can override it to what you need.
Also, this methods are async
. So you can resolve other data.
For example in Post
store when you fetch post by id, you need to fetch comments.
So you can get your comments store (with resolveStore
or by second arg in resolved method) and fetch the comments to specific post.
In addition to those lifecycle methods there is will
lifecycle quavilents.
- storeWillFetchAll
- storeWillFetchOne
- storeWillCreateNew
- storeWillUpdate
- storeWillDelete
This is pretty similar of ES6 api of React component.
Also, we have fail
lifecycle hooks:
- storeFetchAllFailed
- storeFetchOneFailed
- storeCreateNewFailed
- storeUpdateFailed
- storeDeleteFailed
It is useful to track errors and show notifications or manipulate models in some cases.
And there is a methods to get your data from store:
- has
- getMap
- getOne
- asArray (getter/computed)
All stores by default registered with name same as your class name.
So class: class MyStore {}
will be resitered as MyStore
.
To get stores from another stores (as was described in resolvers
serction) you should use stores
from resolvers did
hooks.
Quick example using typescript:
import { serializable, identifier } from 'serializr';
import { BaseDomainStore } from 'striker-store';
import * as React from 'react';
import { observer } from 'mobx-react';
class ExampleUserModel {
@serializable(identifier()) id;
@serializable name;
}
class ExampleUserService {
fetchAll() {
return fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json());
}
fetchOne(id) {
return fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
.then(response => response.json());
}
}
class ExampleUserStore extends BaseDomainStore {
static service = new ExampleUserService();
static modelSchema = ExampleUserModel;
}
const store = new ExampleUserStore();
function doFetch() {
store.fetchAll();
}
function QuickExample() {
return (
<div>
<h2>Fetching</h2>
<div>Do initial fetch pls</div>
<div>
{store.asArray.map(user => <div key={user.id}> {user.name} </div>)}
</div>
<button onClick={doFetch}>Fetch</button>
</div>
);
}
export default observer(QuickExample);
There is an helper components to use react-router
.
Please, check out crud-example
in demo
folder.
Please, check out demo
folder or https://stiker-store.surge.sh
We are welcome to contribute. Just raise a pull request.