- Express-like and promise-based middlewares and routers. See Middleware and Router
- Immutable state and operations. See Immutable State
- Model support to normalize data and operate like database. See Model
// store.js
import { Store, Model } from 'stas';
import router from './router';
const store = new Store({ users: [] });
store.model('User', { posts: ['Post'] });
store.model('Post', { author: 'User' });
store.use(router);
module.exports = store;
// router.js
import { createRouter } from 'stas';
const router = createRouter();
router.all('/users/query', async (req, store, next) => {
const User = store.model('User');
const { offset, limit } = req.body;
const result = await fetch('/api/users/query', { offset, limit });
store.mutate((state) => {
const ids = User.merge(result);
return state.set('users', ids);
});
});
router.all('/users/:userId/posts/create', async (req, store, next) => {
const User = store.model('User');
const Post = store.model('Post');
const { userId } = req.params,
{ title } = req.body;
const result = await fetch(`/api/users/${userId}/posts/create`, { title });
store.mutate((state) => {
const postId = Post.merge(result);
const mUser = User.get(userId).set('posts', posts => posts.push(postId));
User.set(mUser);
return state;
});
});
module.exports = router;
// UserListPage.js
import { PureComponent, PropTypes } from 'react';
import { connect } from 'react-stas';
class UserListPage extends PureComponent {
static propTypes = {
users: PropTypes.array.isRequired,
dispatch: PropTypes.func.isRequired,
}
render() {
const { users } = this.props;
return (<ul>
{users.map(user => <li>
<span>{user.name}</span>
<span>{user.posts.map(post => post.title).join(',')}</span>
</li>)}
</ul>);
}
}
module.exports = connect(({ state, dispatch, props, store }) => {
const User = store.model('User');
const Post = store.model('Post');
const userIds = state.get('users');
const users = User.get(userIds).map((user) => {
user.posts = user.posts.map(postId => Post.get(postId));
return user;
});
return { users };
})(UserListPage);
// index.js
import ReactDom from 'react-dom';
import { Provider } from 'react-stas';
import store from './store';
import UserListPage from './UserListPage';
ReactDom.render(
<Provider store={store}>
<UserListPage />
</Provider>,
document.getElementById('app'),
);
yarn add stas react-stas -S
or if your'd like to use npm:
npm install stas react-stas -S
Like express router but with promise support. For detail see uni-router
Prefix match req.url
with pattern
Exact match req.url
with pattern
It's using plain-immutable to make the state immutable. plain-immutable
is a simple immutable library with array and plain object compatible.
import {immutable} from 'stas';
const arr = immutable([1, 2, { a: 'b' }]);
arr[0] = -1; // it will throw
arr[2].a = 'c'; // it will throw too even you want to change the nested object
console.log(arr[2].a);// b
console.log(JSON.stringify(arr));// [1,2,{"a":"b"}]
const mArr = arr.set(0, -1);
console.log(arr[0]);// 1
console.log(mArr[0]);// -1
Model is used to hold structured data and maintain their relationships. In short, it lets you operate like a database. In the real world app, data are mostly consisted of nested objects. See a post with user and comments:
const post = {
id: 1,
title: 'Hello',
author: {
id: 1, name: 'Tian',
},
comments: [
{ id: 1, text: 'Up', creater: { id: 2, name: 'Jian' } },
{ id: 2, text: 'Thanks', creater: { id: 1, name: 'Tian' } },
],
};
If we want to change the user's name, many locations need to check and change, which is terrible! So we use models to flatten the structure, and use id to keep the records' relationships.
const store = new Store({ post: null });
store.model('User');
store.model('Post', { author: 'User', comments: ['Comment'] });
store.model('Comment', { creater: 'User' });
store.mutate((state) => {
const Post = store.model('Post');
const id = Post.merge(post);
return state.set('post', id);
});
console.log(store.state); // {post: 1}
console.log(store.database);
// { Post: { '1': { id: 1, title: 'Hello', author: 1, comments: [Object] } },
// User: { '1': { id: 1, name: 'Tian' }, '2': { id: 2, name: 'Jian' } },
// Comment:
// { '1': { id: 1, text: 'Up', creater: 2 },
// '2': { id: 2, text: 'Thanks', creater: 1 } } }
Get the record(s). If you pass an array of ids, it will return an array of records. The return is always immutable.
Add or replace the record in database.
Normalize the input data and merge to database. Return the id or ids(if input is array).
Remove the record(s)
Since react-native doesn't support module.hot.accept(path, callback)
but module.hot.accept(callback)
, we have to use a function to export the store, then replace store's middlewares(routers) by utilizing closure.
import Store from 'stas';
import routers from './routers';
export default function createStore() {
const store = new Store()
store.use(routers);
if (module.hot) {
module.hot.accept(() => {
const newRouters = require('./routers').default;
store.clearMiddlewares();
store.use(newRouters);
});
}
return store;
}
Checkout the CONTRIBUTING.md if you want to help
Licensed under MIT
Copyright (c) 2017 Tian Jian