React/ReactNative state management: how to use rematch

Some students reported that state management is not very clear when developing ReactNative applications. In the next few articles, we will compare the use, advantages and disadvantages of several frameworks commonly used in React and ReactNative state management.

The previous article introduced the use of redux-toolkit, an upgraded version of redux . In this article, let's take a look at the well-known redux upgrade library in the community: rematch.

The following is a code example of using React and rematch to create a simple Todo List App, and the complete code is at the end of the article:

  1. First, create a new React application by entering the following command on the command line:
npx create-react-app todolist
  1. Install rematch and react-redux:
npm install @rematch/core react-redux
  1. Create a models.ts file, inherit the Models of rematch in it, and define all model types of the current business
import { Models } from "@rematch/core";

//导出当前业务的所有 model 的类型
export interface RootModel extends Models<RootModel> {
  //暂时先空着
}

//创建并导出所有 model 的实例
export const models: RootModel = {}

In the above code, RootModel is all the model interfaces of the current business.

The model in rematch is similar to the slice concept of redux-toolkit, both represent the initial state of a business and supported operations.

  1. Create a todo.ts file, and use rematch's createModel to create a todo business model:
import { createModel } from "@rematch/core";
import { State, TODO } from "../../module/todo";
import { RootModel } from "./models";

const initState : State = {
    todos: [
        {
            text: "zsx clean room, model init data"
        }
    ]
};

//创建一个 model(类似 redux-toolkit 的 slice),包括一个业务的初始状态、状态处理和变更后的影响
//最核心的地方 **
export const todo = createModel<RootModel> () ({
    name: 'todo',
    state: initState,
    //reducers 需要是纯函数:只依赖参数进行计算,不使用外部数据
    reducers: {
        //与 toolkit 的 slice 不同,参数直接是 payload,更简单
        addTodo: (state: State, payload: string) => {
            //返回新状态
            return {
                ...state,
                todos: [...state.todos, {text: payload}]
            };
        },
        deleteTodo: (state: State, payload: string) => {
            const todos = state.todos.filter((item: TODO, index: number)=> {
                return item.text !== payload
            });
            
            return {
                ...state,
                todos
            }
        }
    },
})

From the above code, we can see that the model in rematch is similar to the slice concept of redux-toolkit, in which the name, initial state and reducers can be specified.

the difference:

  1. The reducer of rematch, the parameter is payload, not action, which is more direct
  2. The reducer of rematch must have a return value, otherwise an error will be reported!
  3. rematch directly exports the return value of createModel, no need to export actions and reducer separately

Again, the model is used in rematch to represent the state management of a certain business. The todo we just created through createModel is a model that represents the state management of the todo business.

  1. Go back to the models.ts file created in the third step, and add the todo we just created to the members of RootModel:
import { Models } from "@rematch/core";
import {todo} from "./todo";

//导出当前业务的所有 model 的类型
export interface RootModel extends Models<RootModel> {
    //这里的名称就是后续调用 dispatch 的名称
    todo: typeof todo
}

//创建并导出所有 model 的实例
export const models: RootModel = {todo: todo}

Through the above code, we can know that among all current business models, there is a model called "todo".

At the same time, the instance models of all exported models also have a member todo, which is the todo model created by createModel earlier.

  1. Use the init function of rematch to create the store:
import { init, RematchDispatch, RematchRootState } from "@rematch/core";
import { models, RootModel } from "./model/models";

//创建 store,参数就是所有业务的 model
export const store = init({
    models
})

store.subscribe( () => {
    console.log('store update >>> ' + JSON.stringify(store.getState()))
})

store.dispatch.todo.addTodo("add from store")

//导出类型,用于业务组件里使用
export type Store = typeof store
export type Dispatch = RematchDispatch<RootModel>
export type RootState = RematchRootState<RootModel>

As you can see from the above code, the parameters of the init function are the object models of all the business models we exported in the previous step.

The store returned by init is essentially the store of redux, so like the redux store, it also supports subscribe and dispatch.

After creating the store, we need to export several types: Store Dispatch and RootState, which are used to obtain state or dispatch behavior in typescript UI components.

7. Like other libraries, provide the store to the component tree through the Provider of react-redux:

import RematchTodoApp from './rematch/RematchTodoApp';
import { store } from './rematch/store';

const root = ReactDOM.createRoot(
  document.getElementById('root') as HTMLElement
);

//分发给子元素
root.render(
  <Provider store={store}>
    <RematchTodoApp/>
  </Provider>
);

RematchTodoApp is the UI component to be created next

8. Create a UI component, in which use react-redux's useSelector and useDispatch to get the status and distribution behavior:

import {useState} from "react";
import { useDispatch, useSelector } from "react-redux";
import { TODO } from "../module/todo";
import { Dispatch, RootState } from "./store";

//业务通过 useSelector 获取数据;通过 useDispatch 分发
const RematchTodoApp = () => {

    //和 toolkit 类似,需要根据 model 名称访问数据
    //参数类型就是 store 里导出的类型
    const todos = useSelector((state: RootState) => {
        return state.todo.todos;
    });

    //和 toolkit 不同的在于,需要声明类型
    //同时分发的时候也有些不同
    const dispatch = useDispatch<Dispatch>();
    const [text, setText] = useState('');

    const handleInput = (e: any) => {
        setText(e.target.value)
    }

    const handleAddTodo = () => {
        // 分发的时候,通过 dispatch.<model name>.<reducer name> 的方式调用,参数就是 payload 的类型
        //toolkit 里的写法:dispatch(addTodo(text))
        dispatch.todo.addTodo(text)
        setText('')
    }

    const handleDeleteTodo = (text: string) => {
        //toolkit 里的写法:dispatch(deleteTodo(text))
        dispatch.todo.deleteTodo(text)
    }

    return (
        <div style={
   
   {display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center'}}>
            <h1>This Is Rematch TODO App.</h1>
            <ul>
                {todos && todos.map((todo: TODO, index: any) => {
                    return (
                        <li key={index}>
                            <span>{todo.text}</span>
                            <button style={
   
   {marginLeft: '12px'}} onClick={() => handleDeleteTodo(todo.text)}>finish</button>
                        </li>
                    )
                })}
            </ul>

            <div style={
   
   {display: 'flex', flexDirection: 'row'}}>
                <input value={text} onChange={handleInput}/>
                <button style={
   
   {marginLeft: '12px'}} onClick={handleAddTodo}>Add Todo</button>
            </div>
        </div>
    )
}

export default RematchTodoApp;

In the above code, we use useSelector to obtain the status required by the current business, because we get the data of all businesses, so we need to get the data belonging to todo through the business name of todo:

    //和 toolkit 类似,需要根据 model 名称访问数据
    //参数类型就是 store 里导出的类型 RootState
    const todos = useSelector((state: RootState) => {
        return state.todo.todos;
    });

It should be noted that the "todo" in state.todo.todos is the name of the key when we add the todo model in the models in the fifth step.

Then we use useDispatch to get dispatch. The difference from toolkit is that the type needs to be declared:

    const dispatch = useDispatch<Dispatch>();

    const handleAddTodo = () => {
        // 分发的时候,通过 dispatch.<model name>.<reducer name> 的方式调用,参数就是 payload 的类型
        //toolkit 里的写法:dispatch(addTodo(text))
        dispatch.todo.addTodo(text)
        setText('')
    }

When distributing behavior, the toolkit is like this: dispatch(addTodo(text)), and the rematch is like this: dispatch.todo.addTodo(text). Personally, I feel that rematch is slightly better, avoiding layers of nesting.

To sum up, there are several steps to manage the state through rematch:

  1. Inherit the Models of rematch to define all model types of the current business

  2. Use rematch's createModel to create a todo business model, declare the initialization state, reducers

    1. The parameters of each reducer are state and payload, which must have a return value
  3. Use the init function of rematch to create a store, and the parameters are all models

    1. Export the type of RematchDispatch RematchRootState and store
  4. Distributed to the component tree through Provider

  5. Use react-redux's useSelector and useDispatch in UI components to get state and distribution behavior

As you can see, rematch and redux-toolkit have great similarities.

Complete code: https://github.com/shixinzhang/redux-sample/tree/main/src/rematch

Guess you like

Origin blog.csdn.net/u011240877/article/details/129777679