React 之 mobx-state-tree(Redux替代品) 状态管理

MST(mobx-state-tree)、redux做多组件间全局state管理(类比vuex,父 孙组件状态传递解耦)。

tree = type + state

树中的每个节点都由两件事来描述: type (事物的形状) 和 data (它当前所处的状态).

最简单的树如下所示:

1.声明类型为`Root`的节点的形状`。model是一个types中最重要的一个type,使用types.model方法得到的就是Model,在Model中,可以包含多个type或者其他Model。

//定义type

const Root = types.model("Root", { str: types.string }); //定义模型形状(name为Root,props为{ str: types.string })

2.基于“Root”类型创建一个具有初始状态和env的树通过树可执行action等。Model.create可以传入两个参数,第一个是Model的初始状态值,第二个参数是可选参数,表示需要给Model及子Model的env对象(环境配置对象),env用于实现简单的依赖注入功能。

//创建model实例

const root = Root.create({str: "test"}, env); //创建model, 传入初始状态值 和 env环境配置对象参数

root.setStr("mst");//调用action

observer 函数/装饰器可以用来将 React 组件转变成响应式组件它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件。 observer 是由单独的 mobx-react 包提供的。@observer 只会增强你正在装饰的组件,而不是内部使用了的组件。 所以通常你的所有组件都应该是装饰了的。但别担心,这样不会降低效率,相反 observer 组件越多,渲染效率越高。

当 observer 需要组合其它装饰器或高阶组件时,请确保 observer 是最深处(第一个应用)的装饰器,否则它可能什么都不做。

实现UI组件尽可能地使用observer装饰组件

使用mobx-react包提供的observer装饰器装饰后的组件能响应observable的变化,并做了诸多的性能优化,尽可能为你的组件加上observer,除非你想要自定义shouldComponentUpdate来控制组件更新时机。

React.shouldComponentUpdate() 方法会返回一个布尔值,指定 React 是否应该继续渲染,默认值是 true, 即 state 每次发生变化组件都会重新渲染。

@observer

export class TableContent extends React.Component<TableContentProps> { }

尽可能地编写无状态组件

有的同学可能看到过类似这样的说法: 用Redux管理全局状态,用组件State管理局部状态。

笔者不认同这种说法,根据笔者的经验来看,当项目复杂到一定的程度,使用State管理的状态会难受到让你抓狂:某个深层次的组件State只能通过改变上层组件传递的props来进行更新。

更何况,现在无状态(state less)组件越来越受到大家的认可,react hooks的出现也顺应了组件无状态的这个发展趋势。

当应用都由无状态组件构成,应用的状态都存储在触手可及的地方(如Redux或MST),想要在某些时刻修改某个状态值就变得轻而易举。

types

MST提供的一个重要对象就是types,在这个对象中,包含了基元类型(primitives types),如string、boolean、number,还包含了一些复杂类型的工厂方法和工具方法,常用的有model、array、map、optional等。

MST可用的类型和类型方法非常多,这里主要介绍model类型,其他的可以在这里查看:类型概述 ·MobX状态树 (mobx-state-tree.js.org)

model是一个types中最重要的一个type,使用types.model方法得到的就是Model,在Model中,可以包含多个type或者其他Model。

一个Model可以看作是一个节点(Node),节点之间相互组合,就构造出了整棵状态树(State Tree)。

types.model 创建一个可链接的model类型,其中每个链接方法都会生成一个新类型

.named(name)克隆当前类型,但为其指定一个新名称

.props(props)基于当前类型生成新类型,并添加/重写指定的属性

.actions(self => object literal with actions)基于当前类型生成一个新类型,并添加/覆盖指定的操作

.views(self => object literal with view functions)基于当前类型生成新类型,并添加/覆盖指定的视图函数

.preProcessSnapshot(snapshot => snapshot)可用于在实例化新模型之前对原始 JSON 进行预处理。

.postProcessSnapshot(snapshot => snapshot)可用于在获取模型快照之前对原始 JSON 进行后处理。

props

props指的是Model中的属性定义。props定义了这个Model维护的状态对象包含哪些字段,各字段对应的又是什么类型。

props中value可以是:

1.一种类型(可以是一个简单的基元类型 或者 复杂类型,也可以是一个预定义类型):

export const Test= types.model("name", {price: types.number});//第一个参数为name属性定义,第二个参数为props属性定义

//等同于

export const Test= types.model("name").props({price: types.number})

//预定义类型Todo

const Test= types.model("name").props({todos: types.array(Todo)})

2.基元

//使用基元作为类型 是 引入具有默认值的属性的语法糖(基元类型是从默认值推断出来的)

const Test= types.model("name").props({endpoint: "http://localhost"}) //等价于endpoint: types.optional(types.string, "http://localhost")

views

views是Model中一系列衍生数据或获取衍生数据的方法的集合,类似Vue组件的computed计算属性

在定义Model时,可以使用model.views方法定义views

export const ProductItem = types

    .model("ProductItem", {

        prodName: types.string,

        price: types.number,

        discount: types.number,

    })

    .views(self => ({

        get priceAfterDiscount () {

            return self.price - self.discount;

        }

}));

const productItem= ProductItem.create({price: 1});

productItem.priceAfterDiscount();

上面代码中,定义了priceAfterDiscount,表示商品的折后价格。调用.views方法时,传入的是一个方法,方法的参数self是当前Model的实例,方法需要返回一个对象,表示Model的views集合。

需要注意的是,定义views时有两种选择,使用getter或者不使用。使用getter时,衍生数据的值会被缓存直到依赖的数据发送变化。而不使用时,需要通过方法调用的方式获取衍生数据,无法对计算结果进行缓存。尽可能使用getter,有助于提升应用的性能

actions

actions是用于更新状态的方法集合。在安全模式下,所有对状态的更新操作必须在actions中执行,否则会报错

const Root = types

    .model("Root", {

        str: types.string,

    })

    .actions(self => ({

        setStr (val: string) {

            self.str = val;

        },

afterCreate () {

            // 执行一些初始化操作

        }

    }));

const root = Root.create({str: "test"});

root.setStr("mst");

除了通常意义上用来更新状态的actions外,在model.actions方法中,还可以设置一些特殊的actions:

afterCreate

afterAttach

beforeDetach

beforeDestroy

从名字上可以看出来,上面四位都是生命周期方法,可以使用他们在Model的各个生命周期执行一些操作

异步Action

异步更新状态是非常常见的需求,MST从底层支持异步action。

const model = types

    .model(...)

    .actions(self => ({

       async getData () { // async/await

            try {

                const data = await api.getData();

            } catch (err) {

            }

        },

        // promise

        updateData () {

            return api.updateData()

                .then(...)

                .catch(...);

        }

    }));

若使用Promise、async/await来编写异步Action,在异步操作之后更新状态时,代码执行的上下文会脱离action,导致状态在action之外被更新而报错。解决办法:编写一个runInAction的action将更新状态的操作放到runInAction中执行

const Model = types

    .model(...)

    .actions(self => ({

        runInAction (fn: () => any) {

            fn();

        },

        async getData () {

            self.runInAction(() => self.loading = true);

            const data = await api.getData();

            self.runInAction(() => {

                self.data = data;

                self.loading = false;

            });

        }

}));

但是在某些情况下,这种方法不够完美:一个异步action被分割成了N个action调用,无法使用MST的插件机制实现整个异步action的原子操作、撤销/重做等高级功能。为了解决这个问题,MST提供了flow方法来创建异步action:

import { types, flow } from "mobx-state-tree";

const model = types

    .model(...)

    .actions(self => {

        const getData = flow(function * () {

            self.loading = true;

            try {

                const data = yield api.getData();

                self.data = data;

            } catch (err) {

            }

            self.loading = false;

        });

        return {

            getData

        };

})

使用flow方法需要传入一个generator function,在这个生成器方法中,使用yield关键字可以resolve异步操作。并且,在方法中可以直接给状态赋值,写起来更简单自然。

Snapshot

snapshot即“快照”,表示某一时刻,Model的状态序列化之后的值。这个值是标准的JS对象。

使用getSnapshot方法获取快照:applySnapshot方法可以更新Model的状态.

import { getSnapshot, applySnapshot } from "mobx-state-tree";

cosnt Model = types.model(...);

const model = Model.create(...);

console.log(getSnapshot(model));

applySnapshot(model, {

    msg: "hello"

});

getSnapshot及applySnapshot方法都可以用在Model的子Model上使用。

Volatile State

在MST中,props对应的状态都是可持久化的,也就是可以序列化为标准的JSON数据。并且,props对应的状态必须与props的类型相匹配。

如果需要在Model中存储无需持久化,并且数据结构或类型无法预知的动态数据,可以设置为Volatile State。Volatile State使用model.volatile方法定义:

import { types } from "mobx-state-tree";

import { autorun } from "mobx";

const Model = types

    .model("Model")

    .volatile(self => ({

        anyData: {} as any

    }))

    .actions(self => ({

      runInAction (fn: () => any) {

        fn();

      }

    }));

const model = Model.create();

autorun(() => console.log(model.anyData));

model.runInAction(() => {

  model.anyData = {a: 1};

});

model.runInAction(() => {

  model.anyData.a = 2;

});

和actions及views一样,model.volatile方法也要传入一个参数为Model实例的方法,并返回一个对象。

代码中使用Mobx的autorun方法监听并打印model.anyData的值,一共看到2次输出:

anyData的初始值

第一次更新anyData后的值

但是第二次为anyData.a赋值并没有执行autorun。

由此可见,Volatile State的值也是Observable,但是只会响应引用的变化,是一个非Deep Observable

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/19882.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Dinky DorisCDC 整库同步到 Doris

doris flinkcdc语法参考 Flink Doris Connector - Apache Doris 参考&#xff1a; Doris Flink DolphinScheduler Dinky 构建开源数据平台_dinky dolphinscheduler flink-CSDN博客

2024年,抖音小店618十大爆款预测!商家抓紧时间上架!

哈喽~我是电商月月 做电商的玩家都知道&#xff0c;一但到了换季或者是节日大促的时候&#xff0c;销量高&#xff0c;是最容易爆单的阶段 而提前上架一些热卖产品&#xff0c;爆单的几率在自己的店铺机会就越大 而最近的一个大型活动&#xff0c;就是618了&#xff0c;抖音…

每日一练 - 路由策略配置工具

01 真题题目 可用于多种路由协议,设定匹配条件,属性匹配后进行设置,由 if-match 和 apply 子句组成 的路由选择工具是? A.IP-Prefix B.community-filter C.as-path-filter D.route-policy 02 真题答案 D 03 答案解析 正确答案是D. Route-Policy。Route-Policy是一种强大的…

只出现一次的数字II ---- 位运算

题目链接 题目: 分析: 对于只出现一次的数字, 他的任意一个bit位, 可能是0或1对于其余出现3次的数字, 假设有3n个数, 那么他们的任意一个bit相加的和可能是3n个0或3n个1那么对于数组中的全部数字的任意一个bit位之和共有三种情况: 3n个1 1 3n13n个0 1 13n个1 0 3n3n个0…

华为认证学习笔记:生成树

以太网交换网络中为了进行链路备份&#xff0c;提高网络可靠性&#xff0c;通常会使用冗余链路。但是使用冗余链路会在交换网络上产生环路&#xff0c;引发广播风暴以及MAC地址表不稳定等故障现象&#xff0c;从而导致用户通信质量较差&#xff0c;甚至通信中断。为解决交换网络…

如何限制上网行为?上网行为管控软件有什么功能?

上网行为的管理与限制对于保障企业安全、提高员工工作效率以及保护孩子健康成长都显得尤为重要。 上网行为管控软件作为一种专业的工具&#xff0c;在这方面发挥着不可替代的作用。 本文将探讨如何限制上网行为&#xff0c;并介绍上网行为管控软件的主要功能。 一、如何限制上…

C++入门——类和对象【3】(6)

前言 本节是C类和对象中的最后一节&#xff0c;学完本节内容并且能够掌握之前所学的所有内容的话&#xff0c;C就可以说是入门了&#xff0c;那我们废话不多说&#xff0c;正式进入今天的学习 1. 再谈构造函数 1.1 引入 我们在栈的背景下来看 栈的代码&#xff1a; ​type…

前端基础1-6 :es6

点击跳转&#xff0c;详细查看es6 const常量 const LIMIT 10; const OBJ_MAP {a: A,A: a }1. 不允许重复声明赋值 var arg1 aarg1 aa// 常量 - ES5Object.defineProperty(window, arg2, {value: aaa,writable: false})// ES6const arg2 aaa2. 块级作用域 if (true) {con…

【技术实操】银河高级服务器操作系统实例分享,达梦数据库服务器 oom 问题分析

1. 服务器环境以及配置 【 机型】 处理器&#xff1a; HUAWEIKunpeng 920 5220 内存&#xff1a; 400518528 kB 主板型号&#xff1a; Chaoqiang K620 series 整机类型/架构&#xff1a; ARM BIOS 版本&#xff1a; KL4.41.028.TF.220224.R 固件版本&#xff1a; KL4.41…

AI能否代替ACE

什么是ACE ? 申请ACE需要以下条件: 1.发表与oracle相关的技术博客 2.参与Oracle相关的技术大会 3.对Oracle社区做出贡献。 这正好是AI应用的场景吗? 在一个群里有个群友质疑AI落地,以及应用领域? Kelvin:我一直在迷茫&#xff0c;学不好。这么多有趣AI 问题&…

Redis-Cluster模式基操篇

一、场景 1、搞一套6个主节点的Cluster集群 2、模拟数据正常读写 3、模拟单点故障 4、在不停服务的情况下将集群架构改为3主3从 二、环境规划 6台独立的服务器&#xff0c;端口18001~18006 192.169.14.121 192.169.14.122 192.169.14.123 192.169.14.124 192.169.14.125 192…

迁移ISE ChipScope逻辑分析器到Vivado硬件管理器

迁移ISE ChipScope逻辑分析器到Vivado硬件管理器 介绍 本章介绍AMD Vivado™Design Suite硬件管理器&#xff0c;以及这些工具之间的关系 到ISE™设计套件ChipScope™逻辑分析器工具&#xff0c;以及如何迁移IP核 从ISE ChipScope环境到Vivado Design Suite。 Vivado硬件管理器…

Gitlub如何删除分支(删除远程分支+本地分支)

目录 背景 删除方法 总结 背景 想要删除自己在本地创建的并已上传到远程分支中的分支。 删除方法 1&#xff09;删除远程分支 git push origin --delete brannchname 2&#xff09;删除本地分支 先切换到其他分支 git checkout otherbranch 删除本地分支 git bran…

使用LLaMA-Factory微调大模型

使用LLaMA-Factory微调大模型 github 地址 https://github.com/hiyouga/LLaMA-Factory 搭建环境 git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory在 LLaMA-Factory 路径下 创建虚拟环境 conda create -p ./venv python3.10激活环境 c…

Spring Boot项目中的Controller、Service、Mapper和Entity层的作用与联系

效率工具 推荐一个程序员常用的工具网站&#xff1a;程序员常用工具&#xff08;http://tools.cxyroad.com&#xff09;&#xff0c;有时间戳、JSON格式化、文本对比、HASH生成、UUID生成等常用工具&#xff0c;效率加倍嘎嘎好用。 云服务器 云服务器限时免费领&#xff1a;轻量…

为参数设置默认值

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 调用函数时&#xff0c;如果没有指定某个参数将抛出异常&#xff0c;为了解决这个问题&#xff0c;我们可以为参数设置默认值&#xff0c;即在定义函…

Oracle Linux上安装ORDS

ORDS就是Oracle REST Data Services。 环境如下&#xff1a; Oracle Linux 8Oracle Database 19cIP地址为A.B.C.D 要安装最新版本的ORDS&#xff0c;当前为24.1.1。 全程参考文档&#xff1a;Installing and Configuring Oracle REST Data Services 安装ORDS 添加reposit…

org.apache.kafka.clients.consumer.CommitFailedException

kafka报org.apache.kafka.clients.consumer.CommitFailedException问题分析 org.apache.kafka.clients.consumer.CommitFailedException: Commit cannot be completed since the group has already rebalanced and assigned the partitions to another member. This means that…

使用反射调用Android隐藏API

前情提要 每一次Android大版本的升级&#xff0c;往往会有大量的APP出现兼容性问题&#xff0c;导致这个情况的主要原因是由于APP的热修复SDKs以及依赖Android internal API(内部API)&#xff0c;也就是非SDK API。这些API是指标记hide的类、方法以及字段&#xff0c;它们不属…

多线程的坑

有以下列表&#xff0c; List shardingCreateTablesList new ArrayList(); shardingCreateTablesList.add(“tb1”); shardingCreateTablesList.add(“tb2”); shardingCreateTablesList.add(“tb3”); shardingCreateTablesList.add(“tb4”); shardingCreateTablesList.add(…