📜 ⬆️ ⬇️

An easy-to-use status container for a Restore application called Xstore.

Dear colleagues, I present to your attention and your conviction a container for managing the state of React of the xstore application. He is definitely such a small children's bike next to the big and glittering Redux motorcycle. We all JavaScript programmers are such a big and fast-paced bicycle factory.


For more or less, just beginning or starting your acquaintance with React JavaScript Redux programmers may seem like a somewhat complicated thing, which is sometimes incomprehensible how it works and which is difficult to "connect", you want something simpler, something like this small bike.


Let's take a closer look at it.


Installation


npm install --save xstore 

Using


First we need to add "handlers" to the repository.
(An example handler is presented in the block below)


index.js


 import React from 'react' import ReactDOM from 'react-dom' import Store from 'xstore' import App from './components/App' import user from './store_handlers/user' import dictionary from './store_handlers/dictionary' //       "_". //       : user  dictionary. Store.addHandlers({ user, dictionary }); ReactDOM.render( <App/>, document.getElementById('root') ); 

The following is an example of the "user" handler.
It contains the "init" reducer, which is needed to determine the initial state of the "user" storage.


./store_handlers/user.js


 import axios from 'axios' const DEFAULT_STATE = { name: 'user', status: 'alive' } /** =============== Reducers =============== */ //       . //     ,     . //   ,     . //    this.props.dispatch('USER_INIT'). const init = () => { return DEFAULT_STATE; } // this.props.dispatch('USER_CHANGED', {name: 'NewName'}) const changed = (state, data) => { return { ...state, ...data } } /** =============== Actions =============== */ // this.props.doAction('USER_CHANGE', {data}) const change = ({dispatch}, data) => { // {dispatch, doAction, getState, state} dispatch('USER_CHANGED', data); } // this.props.doAction('USER_LOAD', {id: userId}) const load = ({dispatch}, data) => { // {dispatch, doAction, getState, state} axios.get('/api/load.php', data) .then(({data}) => { dispatch('USER_CHANGED', data); }); } //          export default { actions: { load, change // remove, save, add_item, remove_this_extra_item, ..... }, reducers: { init, changed // removed, saved, item_added, this_extra_item_removed, ..... } } 

The following is an example of how to connect a component to the repository.


./components/ComponentToConnect/index.js


 import React from 'react' import Store from 'xstore' class ComponentToConnect extends React.Component { render() { //  user  dictionary    . let {user, dictionary} = this.props; return ( <div className="some-component"> .... </div> ) } loadUser(userId) { //    . this.props.doAction('USER_LOAD', {id: userId}); } setUser(userData) { //    . //         . this.props.dispatch('USER_CHANGED', userData); } } //    . const params = { has: 'user, dictionary' } export default Store.connect(ComponentToConnect, params); 

Possible options "params" to connect:


 { //  ,     : has: 'user, dictionary', //  -: has: ['user', 'dictionary'], //       : has: 'user:name|status, dictionary:userStatuses', //  has: ['user:name|status', 'dictionary:userStatuses'], //      ,    . shouldHave: 'user,dictionary', //  shouldHave: ['user', 'dictionary'], //         ,   true. //      "name", "status", "userStatuses"  "user"  "dictionary" flat: true, // ,      ,    flat = true. //      "user_name", "user_status", "dictionary_userStatuses" withPrefix: true, //      . //       ,  "has"   . handlers: { user, dictionary } } 

List of public repository methods:


 import Store from 'xstore' //         : let state = Store.getState(); //         "user": let userState = Store.getState('user'); //   "name"   "user": let userName = Store.getState('user.name'); //     0   "items"   "user": let someItem = Store.getState('user.items.0'); //  : Store.addHandlers({ user: userHandler, dictionary: dictionaryHandler }) //   "load"  "user": //       ,      . Store.doAction('USER_LOAD', {id: userId}); //   "loaded"  "user": Store.dispatch('USER_LOADED', data); //     : Store.connect(Component, params); 

And now about how it works:


The "connect" method creates a new HOC class XStoreConnect, which hides in itself all the logic of interaction between the component and the repository. This class subscribes to changes to the repository and, when there are any changes, it calls the setState method protected from outside calls (for example, through this.refs.xStoreConnect.setState (...)), after which this component is redrawn, thereby updating props wrapped component.
Directly changing the state of the wrapper component this.refs.xStoreConnect.state = something also does not lead to anything, this class can detect embedded data and delete it.


Wrapper component code
 // ....      const LOCAL_OBJECT_CHECKER = {}; const connect = (ComponentToConnect, connectProps) => { let ready = false; let { has, handlers, shouldHave: shouldHaveString, flat, withPrefix } = connectProps; if (!has && handlers instanceof Object) { has = Object.keys(handlers); } let shouldHave = []; if (typeof shouldHaveString == 'string') { shouldHaveString = shouldHaveString.split(','); for (let item of shouldHaveString) { if (item) { shouldHave.push(item.trim()); } } } let doUnsubscribe, doCleanState, stateItemsQuantity; return class XStoreConnect extends React.Component { constructor() { super(); const updater = (state) => { stateItemsQuantity = Object.keys(state).length; if (ready) { this.setState(state, LOCAL_OBJECT_CHECKER); } else { this.state = state; } } doUnsubscribe = () => { unsubscribe(updater); } doCleanState = () => { cleanStateFromInjectedItems(updater, this.state); } subscribe(updater, {has, handlers, flat, withPrefix}); } setState(state, localObjectChecker) { if (state instanceof Object && localObjectChecker === LOCAL_OBJECT_CHECKER) { super.setState(state); } } componentWillMount() { ready = true; } componentWillUnmount() { ready = false; doUnsubscribe(); } render() { let {props, state} = this; let newStateKeysQuantity = Object.keys(state).length; if (stateItemsQuantity != newStateKeysQuantity) { doCleanState(); } for (let item of shouldHave) { if (state[item] === undefined) { return null; } } let componentProps = { ...props, ...state, doAction, dispatch }; return <ComponentToConnect {...componentProps}/> } } } 

Generating handler files from the command line:


 npm install -g xstore xstore create-handler filename 

You can also register in "scripts" in "package.json":


 { scripts: { "create-handler": "node node_modules/xstore/bin/exec.js" } } npm run create-handler filename 

This command will create a file filename.js (if it does not exist) with a template handler code.


That's all, quite simply right? And now you can kick. I will be glad to advice and reasonable criticism, dear colleagues.


')

Source: https://habr.com/ru/post/341768/


All Articles