Redux — the holy cow for modern React Application. But is it enought ? To say the truth — no. Reactish https://blog.codecentric.de/en/2017/12/developing-modern-offline-apps-reactjs-redux-electron-part-3-reactjs-redux-basics/ “Reactish” TODO-list Lets try to build iconic TODO-list application using React only. code is listen in a reverse order // first - create a underlying data structureconst TODOs = [todo1, todo2, todo3]; // second pass into Applicationconst Application = <TodoList todos={TODOs} /> // third define todo listconst TodoList = ({todos}) => (<ol>{todos.map( todo => <Todo key={todo.id} {...todo} /></ol>);// last define Todoconst Todo = (props) => <div>......</div> The key feature here is — . All the data for a child was passed by it’s parent “Redux” TODO-list Lets try to rework this into redux variant This is simplified version of the original Redux’s Todo list // first - create a underlying data structureconst store = createStore({todos: [todo1, todo2, todo3]}); // second pass into Applicationconst Application =<Provider store={store}><ConnectedTodoList/></Provider> // third connect todo listconst ConnectedTodoList = connect(state => ({todos: state.todos}) // <- here we inject the data)(TodoList) // forth define todo listconst TodoList = ({todos}) => (<ol>{todos.map( todo => <Todo key={todo.id} {...todo} /></ol>);// last define Todoconst Todo = (props) => <div>......</div> The key feature here is — at any point you could connect to the global store. Get data, and dispatch events. This decouples a whole application, but, actually, makes parts of an Application less composable. Even more — (another holy cow of React) just , as long redux ignores it’s position in time and space. Component composition will not work A bit more “redux-ish” TODO-list So. I could mention the one moment here, which could be improved. TodoList is too . And Todo is absolutely not . Lets fix it reactish reduxish ....// third connect todo listconst ConnectedTodoList = connect(state => ({todos: idsFrom(state[todos])})// only ID, not all data)(TodoList)...// forth define todo listconst TodoList = ({todos}) => (<ol>{todos.map( todo => <ConnectedTodo key={todo.id} id={todo.id} /></ol>); // use props to get the desired dataconst ConnectedTodoList = connect((state, props) => ({...state.todos[props.id]}) // get the TODO)(Todo) // last define Todoconst Todo = (props) => <div>......</div> This is faaaaar more better. TodoList is invariant to any change in Todo’s data, and TODO element is connected directly to the store. This is also quite cool, cos it creates borders for an event propagation. Redux’s connect for any update, as long it connected directly to the store, and triggered directly. is the start Redux’s connect for any update, as long it is a PureComponent, and will pass only changes, inducted by the Redux Store, not React update. is the end It’s like an octopus, handling and drowning your app. Think about the origins of things. Beginning, and ending. The final battle Lets pick another example. Almost todo list, but with nesting. Here is the link to the sources. _redux - Predictable state container for JavaScript apps_github.com reactjs/redux The key feature here — you got a Node, and that node got a list inside. And to update/edit that list you have to specify , not only . nodeId listId And redux failed to solve this problem. They could not “normally” provide dispatch with nodeId prefilled, and they shall not provide nodeId for List, to let List select data from the store. purely What they could do? They use handlers to do work. React Redux handleAddChildClick = e => {// get nodeId and dispatch function from a parentconst { addChild, createNode, id } = this.props;const childId = createNode().nodeId;// dispatch event with both variables knownaddChild(id, childId)} This breaks component approach. This is fail. Restate to the rescue Redux-restate, Redux-tree, re-store, Redux-Lenses, Transformation… redux is missing all these things, and is all these things. Restate Restate is a brand new library, which enables your application to become both more Reactish, and more Reduxish. And solves the problems of the last example. How? Lets create an example: ....// third connect todo listconst ConnectedTodoList = connect(state => ({todos: idsFrom(state[todos])})// only ID, not all data)(TodoList)...// forth define todo list Mapperconst TodoList = ({todos}) => (<ol>{todos.map( todo => <ReconnectedTodo key={todo.id} id={todo.id} /></ol>); // create a new redux connection point ( )// "focus" into the derived state HERE IS THE MAGIC const ReconnectedTodo = (// keep only selected TODO in the store // `restore` TODO id is on dispatch(dispatch, event, props) => dispatch({...event, id: props.id}))(ConnectedTodo); reduxFocus (state, pros) => { todo: state.todos[id] }, // use props to get the desired dataconst ConnectedTodoList = connect((state) => ({...state.todo}) // a single todo HERE, the right one)(Todo) // last define Todoconst Todo = (props) => <div>......</div> PS: reduxFocus is a part of Redux Restate. This example uses — this simpler version of , which is a “react” version of . react-redux-focus react-redux-restate redux-restate This solves main redux (global state) issue: reusability. Get the control, form sub-states, sub-stores. Map and reduce. Compose. May be you already have a component, tightly bound to some state structure just by fetching some keys from the state? You know — that component is not reusable. Throw it away connected const ConnectedXComponent = connect(mapStateToProps)(MyXComponent); ....// just wrap it with redux state "adapter". Why not? const ReusableXConnectedComponent = reduxFocus((state,props) => createXState(state, props))(ConnectedXComponent) What it does? creates the new store from the current one, the “bigger” state for the children. ConnectedTodo will have to accept only a single TODO, and it will be isolated from any other data, it does not need. reduxFocus focusing (ie will be focused) In the same time, as long reduxFocus scopes the data, there is no need to pass down “id”, to use it in dispatch, as long you could all to the original store with you know. augment events passing back todoId Thus also means, that you could place TODO-list in a Trello-like tree, just define to get a single TODO-list from a “big” store, and place “nodeid” to all the events, a nested component will dispatch in a future. lense This will enable Component model, so beloved by everyone. More complex example? React-redux-focus is a simple HOC, designed to work with a single store. The original is designed to work with more the one store. But why?? React-redux-restate const mapStateToProps = state => {something: memoizedWithReselect(someComplexOperation(state))}connect(mapStateToProps)(Component) How often Component’s method be called? Everytime some update will pass memoization, and trigger update. So — not often. render real How often will be called? Everytime state got updated. Even if you will also memoize it — you will call memoized function… a lot. From ReactRedux method. someComplexOperation render If you have 100500 connection to the store — all of them will call mapStateToProps on every store update. Unless you will specify areStatesEqual, but you will not. IS that — a “lensed” small part of a global store for the children, and isolate them from the rest. Restate areStatesEqual // default store will be accessible as `default`.const NestedStore = restate({}, ({ default:state }) => ({ state.only, state.data, state.i, state.need }))(RenderChildren); But next you might need the original data once again. But also might need the computed data from the , sintetic store. current const Reprovider = reprovider('superStore'); // copy store to the superStore const NestedNestedStore = restate({ base: 'superStore' }, // get `superStore` as `base`({ default: state, base }) => ({ ...state, base.dataFromBase})(dispatchers, event, props) => {dispatch dispatches.default or dispatcher.base?})... This is not the best example, but sometimes it is possible to have to stores in a single application, for example “Application” and “Page”, and work restate will enable you to combine the data from both of them, and route dispatch back to the correct store. Even more — two stores is common way to work with MobX Is it still redux? The good question, as long few things may break the core concepts. Is it ok to create derivered store from a store? In database world it called views or mat-views(as long they are memoized). You do the same with mapStateToProps. It is ok from a theory point of view. 2. Is it ok to dispatches back? Bubbling yet again? route First — there is no other way. Second — as long routeDispatch is a pure function (same as mapStateToProps or reducers) — it is _predictable_ . As long store behavior is predictable — it is ok. 3. Is it ok to make containers sensitive to the place in a tree? This part was missing. With restate application can use redux in a better way — more often, more easier, more correct. 4. Is it a real store? redux No. This is just a view with redux-like public interface, reusable by redux’s connect down the tree. It’s fake. The current way to solve some sort of tasks is just not correct. Recall the Tree-example — not React, not Redux, just hacks around. You don’t need Redux? It could sounds a bit odd, but You can merge one or more states together, or you can mapping dispatch to React’s component methods. Restate is ok without Redux. form a new state from props, (/* <ReduxFocus focus={ }> */<ReduxFocus focus={ .getState}><ReduxDelay timeout={500}>counter: <Counter /> /* will connect to "redux" */</ReduxDelay></ReduxFocus>); return bigState => smallState this So restate — is a powerfull state management library. Fully React and Redux ideologically compatible. Time to try? Restate was releases literally yesterday, but it is ready. The original repo contains more examples, documentation, and a bit more theory about. _restate - Make redux composable again!_github.com theKashey/restate Let redux be composable again! Let the component architecture bloom! I’ve got a 3(already 5) package for you. Pick the one you need: // to low-level redux manupulationsimport reduxRestate from 'redux-restate'; // to work with multiple storesimport reactReduxRestate from 'react-redux-restate'; // to focus a lens on a single storeimport reactReduxFocus from 'react-redux-focus'; // to freeze the timeimport reactReduxSemaphore from 'react-redux-semaphore'; // to optimize state changesimport reactReduxDelay from 'react-redux-delay'; // to optimize update propagationimport reactReduxUnbranch from 'react-redux-unbranch'; PS: The actual code is just 10–50 LoC per componet. Somethings might become more clear, if you will just read the code. Follow up article _In other words — Fractal State. To be more concrete — Redux Fractal State. It is possible to create Fractal using…_blog.cloudboost.io The State of the State of the State
Share Your Thoughts