一文详解ReactNative状态管理rematch使用
作者:张拭心
引言
有同学反馈开发 ReactNative 应用时状态管理不是很明白,接下来几篇文章我们来对比下 React 及 ReactNative 状态管理常用的几种框架的使用和优缺点。
上一篇文章介绍了 redux 的升级版 redux-toolkit 的使用,这篇文章我们来看下社区里比较出名的 redux 的升级库:rematch。
React 和 rematch 创建Todo List App
下面是使用 React 和 rematch 创建一个简单的 Todo List App 的代码示例,完整代码见文章末尾:
- 首先,在命令行中输入以下命令新建一个 React 应用:
npx create-react-app todolist
- 安装 rematch 和 react-redux:
npm install @rematch/core react-redux
创建一个 models.ts 文件
- 在其中继承 rematch 的 Models,定义当前业务的所有 model 类型
import { Models } from "@rematch/core"; //导出当前业务的所有 model 的类型 export interface RootModel extends Models<RootModel> { //暂时先空着 } //创建并导出所有 model 的实例 export const models: RootModel = {}
在上面的代码中,RootModel 是当前业务的所有 model 接口。
rematch 中的 model 和 redux-toolkit 的 slice 概念类似,都表示一个业务的初始状态和支持的操作。
创建一个 todo.ts 文件
- 在其中使用 rematch 的 createModel 创建一个 todo 的业务 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 } } }, })
从上面的代码中我们可以看到,rematch 中的 model 和 redux-toolkit 的 slice 概念类似,在其中可以指定名称、初始状态 和 reducers。
不同之处:
- rematch 的 reducer,参数是 payload,而不是 action,更加直接
- rematch 的 reducer,必须有返回值,否则会报错!
- rematch 直接导出 createModel 的返回值,不需要分别导出 actions 和 reducer
再次强调一下,rematch 中使用 model 表示某个业务的状态管理,我们刚才通过 createModel 创建的 todo 是一个 model,表示 todo 业务的状态管理。
回到第三步创建的 models.ts 文件,把我们刚才创建的 todo 添加到 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}
通过上面的代码,就可以知道,当前所有业务 model 里,有一个叫做“todo” 的 model。
同时,导出的所有 model 的实例 models,也有一个成员 todo,它就是前面通过 createModel 创建的 todo model。
使用 rematch 的 init 函数创建 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>
从上面的代码可以看到,init 函数的参数就是我们上一步导出的所有业务 model 的对象 models。
init 返回的 store 本质上就是 redux 的 store,因此和 redux store 一样,也支持 subscribe 和 dispatch。
创建完 store 以后,我们还需要导出几个类型:Store Dispatch 和 RootState,他们用于在 typescript UI 组件里获取状态或者分发行为。
和其他库一样,通过 react-redux 的 Provider 将 store 提供给组件树:
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 要创建UI 组件
创建 UI 组件,在其中使用 react-redux 的 useSelector 和 useDispatch 获取状态和分发行为:
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;
在上面的代码中,我们使用 useSelector 获取当前业务需要的状态,因为拿到的是所有业务的数据,因此需要通过 todo 的业务名称获取到属于 todo 的数据:
//和 toolkit 类似,需要根据 model 名称访问数据 //参数类型就是 store 里导出的类型 RootState const todos = useSelector((state: RootState) => { return state.todo.todos; });
需要注意的是,state.todo.todos 里的「todo」是我们第五步在 models 里增加 todo model 时 key 的名称。
随后我们使用 useDispatch 获取 dispatch,和 toolkit 不同的在于,需要声明类型:
const dispatch = useDispatch<Dispatch>(); const handleAddTodo = () => { // 分发的时候,通过 dispatch.<model name>.<reducer name> 的方式调用,参数就是 payload 的类型 //toolkit 里的写法:dispatch(addTodo(text)) dispatch.todo.addTodo(text) setText('') }
在分发行为的时候,toolkit 是这样:dispatch(addTodo(text)),rematch 是这样:dispatch.todo.addTodo(text) ,个人感觉 rematch 这种略微好一点,避免了层层嵌套。
总结
通过 rematch 管理状态分这几步:
- 继承 rematch 的 Models,定义当前业务的所有 model 类型
- 使用 rematch 的 createModel 创建一个 todo 的业务 model,声明初始化状态、reducers
- 每个 reducer 的参数是 state 和 payload,必须有返回值
- 使用 rematch 的 init 函数创建 store,参数就是所有 model
- 导出 RematchDispatch RematchRootState 和 store 的类型
- 通过 Provider 分发给组件树
- 在 UI 组件中使用 react-redux 的 useSelector 和 useDispatch 获取状态和分发行为
可以看到,rematch 和 redux-toolkit 有很大的相似度。
以上就是一文详解ReactNative状态管理rematch使用的详细内容,更多关于ReactNative状态管理rematch的资料请关注脚本之家其它相关文章!