Redoor: New Alternative to Redux

Written by rubender | Published 2021/07/03
Tech Story Tags: state-management | redoor | programming | javascript | javascript-development | react-redux | react | learn-react

TLDR The state management library for React is still under development, but you can already try it. Let’s create the main component, main.js. We need the Store to initate the library, and also here; we specify all the necessary files with actions.js and actionsSetup.js. In our example, we can change the. component directly from the. store directly from. the. input button. The. input function returns new state variables. The. TodoList function is called TodoRun and redoor adds the. variables and all variables.via the TL;DR App

I bring to your attention the state management library for React. The library is still under development, but you can already try it.

Let’s start with an example of everyone’s favorite TODO organizer. Source code on Github. First, let’s create the main component, main.js.

// main.js
import React, { createElement, Component, createContext } from 'react';
import ReactDOM from 'react-dom';
import {Connect, Provider} from './store'
import Input from './InputComp'
import TodoList from './TodoList'
import LoadingComp from './LoadingComp'

const Main = () => (
  <Provider>
    <h1>Todo:</h1>
    <LoadingComp>
      <TodoList/>
    </LoadingComp>
    <hr/>
    <Input/>
  </Provider>
)

ReactDOM.render(<Main />, document.getElementById("app"));

Next, store. We need the Store to initialize the library, and also here; we specify all the necessary files with actions. In our example, this is actions.js and actionsSetup.

// store.js

import React, { createElement, Component, createContext } from 'react';
import createStoreFactory from 'redoor';

// Exporting all functions from actions.js и actionsSetup.js
import * as actions from './actions'
import * as actionsSetup from './actionsSetup'

// here we specify the necessary functions of the React library
const createStore = createStoreFactory({
  Component, 
  createContext, 
  createElement
});

// creating a store as a parameter, you must specify an array of objects
// of all used action functions
const { Provider, Connect } = createStore([
  actions,
  actionsSetup
]);

export { Provider, Connect };

File with our actions and project status

// actions.js

// each local state can contain its own set of variables
// redoor will automatically add them to the global store
// initState is a reserved variable it can be either an object,
// or a function that returns an object with a state
export const initState = {
    todos:[],
    value:'',
}
// adding a new task to the array
// the state variable contains the global state
// the args variable depends on the values passed from the component
// the function returns new state variables
export const a_enter = ({state,args}) => {
  let {value,todos} = state;
  todos.push({
    id:(Math.random()+"").substr(2),
    value:value,
    done:false
  });
  return {
    value:'',
    todos
  }
}

export const a_done = ({state,args}) => {
  let {todos} = state;
  let id = args.id;
  todos = todos.map(it=>(it.id === id ? (it.done = !it.done, it) : it))
  return {
    todos
  }
}

export const a_delete = ({state,args}) => {
  let {todos} = state;
  let id = args.id;
  todos = todos.filter(it=>it.id !== id)
  return {
    todos
  }
}

Components of views

// InputComp.js
import React from 'react';
import {Connect} from './store'

// redoor adds the cxRun function and all variables to the props
// globally store
const Input = ({cxRun, value})=><label className="input">
  Todo:
  
// here we can change the store directly from the component
  <input onChange={e=>cxRun({value:e.target.value})} 
					value={value} 
					type="text" 
  />
  
// by clicking, we call the action a_enter from actions.js
  <button onClick={e=>cxRun('a_enter')} disabled={!value.length}>
		ok
	</button>
</label>

// соеденяем с redoor наш компонент и экспортируем 
export default Connect(Input);

cxRun can operate in two modes. The first is to directly change the store's contents, as in the case of a string parameter or calling an action from a file actions.js.

And the last component that outputs the to-do list itself.

// TodoList.js
import React from 'react';
import {Connect} from './store'

const Item = ({cxRun, it, v})=><div className="item">
  // we call the a_done action, where we specify as a parameter
  // array element in the asense this variable will be called args
  <div className="item_txt" onClick={e=>cxRun('a_done',it)}>
    {v+1}) {it.done ? <s>{it.value}</s> : <b>{it.value}</b>}
  </div>
  <div className="item_del" onClick={e=>cxRun('a_delete',it)}>
    &times;
  </div>
</div>

const TodoList = ({cxRun, todos})=><div className="todos">
  {
    todos.map((it,v)=><Item key={v} cxRun={cxRun} it={it} v={v}/>)
  }
</div>

export default Connect(TodoList);

In our project, there are only two variables in the global store, value, and todos. They are initialized by initState in the file actions.js. initState can be an object or a function that should return an object with a state. Here it is important to understand that all the states in the action file are placed in a single object and each action has access to any state variables.

Actions are functions that must start with the prefix “ a_ “ or “action”. The name of the action function will be specified as the first parameter when calling cxRun. The input parameter will be an object with the state and args variables.

state — this is the entire global state of the project

args is the second parameter of the call to the cxRun function. In our project, when you click delete, we call cxRun(‘a_delete’, it), where the first argument is the name of the action function, and the second is the element itself, which is what we get in args.

The action should return a new state of the state, which will automatically redraw the components that are connected to the store.

What should I do if the action works asynchronously? To do this, we need to connect the setState method to the local variables of the actions file.js using the bindStateMethods function.

//actions.js
let __setState;
let __getState;

// connecting the methods of working with the state
export const bindStateMethods = (getState, setState) => {
  __getState = getState;
  __setState = setState;
};

export const a_setup = async ({state,args}) => {
  __setState({loading:true});
  let data = await loading();
  __setState({
    loading:false,
    todos:data
  })
}

When you call the “a_load” action, the download icon will appear before the download starts, and after the data is loaded, the data array will be updated, and the download icon will be disabled. If you need to get a global state inside an asynchronous function, you can call __getState, which returns the current state.

Debugger

For debugging, there is a redoor-devtool tool. A debugger is a server that listens to data from the redoor library and passes it to a single page at localhost:8333. Thus, the debugger can be located not only in another browser but also on another machine. This is especially useful when developing for mobile devices.

> yarn add redoor-devtool

in a separate console, run the console debug server

> npx redoor-devtool -o

The “-o “ key will open chrome at http://localhost:8333, where the debugger will be.

Conclusion

On my own, I can share that I have already done several projects using this library. It was quite convenient to work with her on a project with sockets. There are, of course, features of use. For example, you need to remember that all actions are “visible” from all modules. This will not be a problem if you have a clear structure for naming actions. In my projects, I use a_moduleName_actionName.

That’s all for now. If you are interested, I will try to write a more detailed review.


Also published on Medium.


Written by rubender | ReactJS, JavaScript, WPA, SPA, web-developer
Published by HackerNoon on 2021/07/03