Application state manager based on PureComponent and React Context Api (React >= 16). Like Redux.
React context: https://reactjs.org/docs/context.html
npm i --save react-app-context
It is one function
import initStorage from 'react-app-context';
//...
const {
Provider,
connect,
Context
} = initStorage(
defaultState,
actions,
property
);
// ...
defaultState
- object, which contains initialize state
{
app: { ... },
users: { ... },
categories: { ... }
// ...
}
actions
- object, which contains actions functions. Each action will get state as first argument and should return new object, which will be put to context object. Action can be async function or return Promise.
{
app: {
// all actions in this path will get Context.app state part
setName: (state, newName) => ({ ...state, name: newName })
// ...
},
users: {
// all actions in this path will get Context.users state part
addUser: (state, name, login, pass) => { ... }
// ...
}
// all actions in root will get full context state object
removeStatePart: (fullState, key) => ({ ...fullState, [key]: null })
// ...
}
property
- object with options
{
debug: false // if true, debug then messages will put to console
}
This function returns object with
{
Provider, // react component
connect, // function to connect your react component to state
Context // React Context Api object
}
Provider
- simple React pureComponent, which shoud be around components, which will be get state of this Provider.
// ...
<Provider>
<MainComponentOfApp />
</Provider>
// ...
connect
- it is HOC function, which returns PureComponent above your component.
function(getNewState: function(state, props), dispatchActions: object): function(Component)
getNewState
- (state, props) => ({ ... })dispatchActions
- { myAction, ... }
// ...
export default connect(
(state, props) => ({
value: state.app.value,
isFetching: state.app.requests[props.id].isFetching
}),
{ myAction }
)(MyComponent);
// ...
// storageConfig.js
import initStorage from 'react-app-context';
import appStorage, { actions } from './appStorage.js';
const Storage = initStorage(
{ app: appStorage },
{ app: actions },
{ debug: false }
);
export {
connect: Storage.connect
}
export default Storage.Provider;
// appStorage.js
export default const initState = {
value: 10,
list: [],
state: 'init',
isLoading: false
};
export function setValue(state, newValue) {
return {
...state,
value: newValue
};
}
export async function getData(state) {
// it is your api function, for example
// `this` will contains
// {
// dispatch: function(function, statePropKey: string): function, - generates function with selected latest state
// actionsMap: Map[function, function], - contains all registered Actions
// call: function(function, ...args) - use to call Action
// getState: function: object - get latest state
// setState: function: Promise - works like setState of Component/PureComponent, but can be called like `await this.setState({ ... });`
// }
dispatch(state => ({ ...state, isLoading: true }), 'app')();
const newList = await myApi.getList();
return {
...state, // or ...this.getState()
list: newList,
isLoading: false
};
}
export const actions = {
setValue,
getData
};
// App.jsx
import React from 'react';
import Provider from './storageConfig';
import Toolbar from './Toolbar/Toolbar';
function App() {
return (
// all Providers childs can use `connect` function from `./storageConfig`
<Provider>
<Toolbar />
</Provider>
);
}
export default App;
// MyButton.jsx
import React from 'react';
import { connect } from './storageConfig';
import { setValue } from './appStorage';
class MyButton extends React.PureComponent {
// `setValue` was described like `(state, newValue) => ...`
// but will be dispatched into component without state arg
// like `(newValue) => ...`
onClick = () => this.props.setValue(Math.round(Math.random() * 1000));
render() {
const { value } = this.props;
return (
<button type="button" onClick={this.onClick}>
{value}
</button>
)
}
}
export default connect(
state => ({ value: state.app.value }),
// setValue will be put into component without first argument (it was `state`)
{ setValue }
)(MyButton);
react-app-context
can be used as external component state