Introduction of redux state management framework zoro

Keywords: React github axios

I am a long-term user of DVA framework. I like the encapsulation of redux by DVA very much. But I encounter a lot of problems which are not easy to handle in the process of using dva. So I write a set of dva-like libraries by myself and try to solve the shortcomings in the process of using dva. Later, I will introduce the problems encountered in the process of using DVA and elaborate zoro How to solve this problem?

This article is suitable for readers who have a certain redux foundation or have used dva

Sketch

  • Minimalist api for quick start
  • Simplifying the application difficulty of redux
  • Quick access to the native widget, wepy widget framework, taro widget framework, in principle, where redux can be used can be accessed
  • Built-in plug-ins to support custom writing plug-ins
  • Global Error Handling Scheme

Links to relevant information are listed below:

  • zoro github warehouse address
  • zoro-plugin Relevant plug-in github warehouse address
  • redux redux warehouse address
  • dva dva library link

The Origin of zoro's Name

zoro, whose full name is Roronoa zoro, is a swordsman in the animation onepiece. He has no demon fruit of his own, and he is constantly moving toward the goal of the world's largest swordsman, which finally proves his constant growth.

Why write zoro Libraries

I mainly list the shortcomings I have encountered in the process of using dva. It is purely my personal opinion. If there are any mistakes, I hope to point out that

1. When DVA calls model externally, dispatch needs to give type type type, which is not intuitive enough.

First, let's look at the columns given by dva:

import React from 'react'
import { connect } from 'dva'
import ProductList from '../components/ProductList'

const Products = ({ dispatch, products }) => {
  function handleDelete(id) {
    // Method of calling model action or effect in dva
    dispatch({
      type: 'products/delete', // model namespace needs to be specified
      payload: id,
    })
  }
  return (
    <div>
      <h2>List of Products</h2>
      <ProductList onDelete={handleDelete} products={products} />
    </div>
  )
}

export default connect(({ products }) => ({
  products,
}))(Products)

And the way I want to use it in zoro is as follows:

import React from 'react'
import { connect } from 'react-redux'
import { dispatcher } from '@opcjs/zoro'
import ProductList from '../components/ProductList'

const Products = ({ dispatch, products }) => {
  function handleDelete(id) {
    // The main difference is here.
    dispatcher.products.delete(id)
  }
  return (
    <div>
      <h2>List of Products</h2>
      <ProductList onDelete={handleDelete} products={products} />
    </div>
  );
};

export default connect(({ products }) => ({
  products,
}))(Products);

2. dva introduces saga libraries as the basis, which increases the threshold of entry (though not too difficult).

Definition of model in dva

export default {
  namespace: 'products',
  state: [],
  effects: {
    *delete({ payload: { id } }, { call, put }) {
      yield call(deleteProductFromServer, { id })
      yield put({ type: 'delete', payload: { id } })
    },
  }
  reducers: {
    delete(state, { payload: id }) {
      return state.filter(item => item.id !== id)
    },
  },
};

zoro introduces async in es7 grammar, await instead of saga

export default {
  namespace: 'product',
  state: [],
  effects: {
    async delete({ payload: { id } }, { put }) {
      await deleteProductFromServer({ id })
      put({ type: 'delete', payload: { id } })
    },
  },
  reducers: {
    delete({ payload: id }, state) {
      return state.filter(item => item.id !== id)
    },
  },
}

3. The synchronous execution of put in DVA can not be realized in dva1. In dva2, the author provides corresponding solutions, but it is still troublesome.

Here is the implementation of dva2. Waiting for the last put to be executed through take, then continue to execute.

yield put({ type: 'addDelay', payload: { amount: 2 } })
yield take('addDelay/@@end')
const count = yield select(state => state.count)
yield put({ type: 'addDelay', payload: { amount: count, delay: 0 } })

In zoro, async, await, all effect methods are inherently promise objects, so synchronous execution of put is also supported.

await put({ type: 'addDelay', payload: { amount: 2 } })
const count = select().count
await put({ type: 'addDelay', payload: { amount: count, delay: 0 } })

How to access zoro quickly in react application

If you are a dva user, or you are a front-end bull, the following description can be ignored, go directly to the warehouse to view the api documentation, there is a more comprehensive api introduction

The access code is as follows

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import zoro from '@opcjs/zoro'
import testModel from './models/test'
import App from './components/App'

const app = zoro()
app.model(testModel) // Register a single model
// app.model([model1, model2]) // Register multiple models

const store = app.start() // Start and create store

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

From the code, it can be found intuitively that only four simple steps are needed to access zoro to react application.

import zoro from '@opcjs/zoro'

const app = zoro()

Introduce zoro library and create app

import testModel from './models/test'

app.model(testModel)

In order to introduce model into app, we ignore how to define model first, and we will focus on it later.

const store = app.start()

Generate redux store objects for injection into the interface layer

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

The generated store is injected into the interface component through react-redux Provider

What is a model, and how do we define our model?

Models are used to store interface data, but also to manage data, including add, delete, check, asynchronous access to data, etc. Now I will use todos application to explain how to define our model.

The simplest model application

The first thing we need to do is to display the todo list and delete it.

Understanding the requirements, let's first define the todos model

export default {
  namespace: 'todos', // Give the model a name that must be unique and invariant
  state: [], // The initial value of the model, which can be of any type
  reducers: {
    add({ payload: { text } }, state) {
      return state.concat([text])
    },
  },
}

Register our model into app

import todos from '../models/todos'
app.model(todos)

...Eliminate other steps, as detailed in the previous section

Next we need to refine our Todos components

import React from 'react'
import { connect } from 'react-redux'
// Introducing dispatcher
import { dispatcher } from '@opcjs/zoro'

const Todos = ({ todos }) => {
  // Trigger a todos add event
  const handleAdd = () => dispatcher.todos.add({ text: 'This is a new agency event.' })
  return (
    <div>
      <ul>
        {todos.map(todo => (
          <li key={todo}>todo</li>
        ))}
      </ul>
      <button onClick={handleAdd}>Add to</button>
    </div>
  )
}

// Link state data to components via connect
export default connect(({ todos }) => ({
  todos,
}))(Todos)

With only one last step, we have completed this requirement by linking the Todos component to the App component.

import React from 'react'
import Todos from '../components/Todos'

const App = () => <Todos />

export default App

This is where the simplest todos requirement is completed, and we have a general understanding of the basic definition of the model. If we need to get the todo list on the server at the beginning of the application, how can we complete it?

How to Interact with Server through model

First of all, we added a new todos service for the model

export default {
  namespace: 'todos', // Give the model a name that must be unique and invariant
  state: [], // The initial value of the model, which can be of any type
  reducers: {
    add({ payload: { text } }, state) {
      return state.concat([text])
    },
    // Add update reducers
    update({ payload }) {
      return payload
    },
  },
  effects: {
    // Add queryTodos to retrieve list data from the server
    async queryTodos({ payload }, { put, select }) {
      // Eliminate getTodosFromServer, which is an ajax request, such as axios, fetch, etc.
      //Communicate with the server, return the list, which must return the Promise object
      const { todos } = await getTodosFromServer(payload)
      // Get the current todos list through select, and see the api documentation for the use of select
      const currentTodos = select()
      // put has many functions, which can initiate the reducer, effect of this model.
      // External model s can also be initiated, but namespace needs to be specified
      // For example, put ({type:'test/update'}) initiates a method in the test model
      // If put initiates an asynchronous effect, wait for the result through await if necessary
      // See the api documentation for more usage
      put({ type: 'update', payload: currentTodos.concat(todos) })
    },
  },
}

After defining the model, we just need to call the method when the component is mounted.

import React from 'react'
import { connect } from 'react-redux'
// Introducing dispatcher
import { dispatcher } from '@opcjs/zoro'

class Todos extend React.Component {
  componentDidMount() {
    // Initial mount call to obtain asynchronous data
    dispachter.todos.queryTodos()
  }

  handleAdd() {
    dispatcher.todos.add({ text: 'This is a new agency event.' })
  }

  render () {
    return (
      <div>
        <ul>
          {this.props.todos.map(todo => (
            <li key={todo}>todo</li>
          ))}
        </ul>
        <button onClick={handleAdd}>Add to</button>
      </div>
    )
  }
}


// Link state data to components via connect
export default connect(({ todos }) => ({
  todos,
}))(Todos)

This is the end of a simple zoro access tutorial. There are many interesting and useful features that can't be introduced, such as plug-in mechanism, global error capture, etc. This is my first tutorial. I don't know if the conditioning is clear and easy to understand. I hope you can see the officials and see if there are any problems, please correct them. Let's make progress together and constantly create more and simpler wheels.

At present, zoro v2 version has been applied in practical small program projects


xmpg.jpeg

Installation methods and more use tutorials are detailed in github zoro

Last but not least, open source is not easy. If it's easy to use, please give it a star. Thank you.

Posted by gojakie on Mon, 06 May 2019 03:25:39 -0700