日常使用mobx的小技巧

由于自己开发的项目都是中小型项目,所以在技术选型上使用了mobx。但是使用过程中发现关于mobx的技术文章并不多。于是萌发出写这篇文章的想法。请轻喷。

  • 更新控制store渲染的方法
mobx一些有关资料

中文文档

github仓库

npm地址

添加mobx
  1. npm i mobx
  2. npm i mobx-react

mobx用来操作store(也就是数据操作层,model层),而mobx-react则是用来操作view(也就是视图层,Component层)。

mobx常用操作符
  1. observable,将JS基本数据类型、引用类型、普通对象、类实例、数组和映射,转换为可观察数据。
  2. action,用来修改observable的数据的动作,只有actionrunInAction才能修改observable
  3. runInAction,用来在异步的时候执行修改observable的数据的动作。例如网络请求后修改数据。
  4. computed,根据现有的observable的值或其它计算值衍生出的值。只有在view使用了computed的值,computed才会执行计算
mobx-react常用操作符
  1. observer,将 React组件 转变成响应式组件。
  2. inject,将组件连接到提供的stores 。一般是用来连接到上层组件提供的store或者全局store
  3. Provider,它是一个react组件,用来向下传递 stores。任意子组件可以使用inject来获取Providerstore
代码

下面会贴点自己的代码。希望能给大家带来一些帮助。

全局store

  1. // 文件 index.jsx
  2. import { Provider } from "mobx-react";
  3. import * as stores from "../../stores";
  4. class MainView extends Component {
  5. render() {
  6. return (
  7. <React.Fragment>
  8. <Provider {...stores}> // 这里是全局stores的配置
  9. <Switch>
  10. {routers.map((item, index) => {
  11. return (
  12. <Route
  13. exact
  14. key={item.path}
  15. path={item.path}
  16. component={item.component}
  17. />
  18. );
  19. })}
  20. </Switch>
  21. </Provider>
  22. </React.Fragment>
  23. );
  24. }
  25. }
  26. // 文件 ../../stores/index.js
  27. import aStore from "./aStore";
  28. import bStore from "./bStore";
  29. export { aStore, bStore};
  30. // 文件 aStore.js
  31. class AStore {
  32. @observable info = {};
  33. @observable line = {};
  34. @action
  35. onInfo = data => {
  36. this.info = data;
  37. };
  38. @action
  39. onLine = data => {
  40. this.line = data;
  41. };
  42. }
  43. const aStore = new AStore();
  44. export default aStore;
  45. // 组件使用aStore
  46. @inject(all => ({
  47. aStore: all.aStore // 连接到aStore
  48. }))
  49. @observer
  50. class Detail extends Component {
  51. updateInfo = () => {
  52. const aStore = this.props.aStore;
  53. aStore.onInfo(info) // 改变store属性
  54. }
  55. render() {
  56. const aStore = this.props.aStore; // 获取到全局store
  57. return (
  58. <div>
  59. {aStore.info.name} // 使用里面的属性
  60. <Button onClick={this.updateInfo}>改变info</Button>
  61. </div>
  62. );
  63. }
  64. }

组件内可观察数据。

  1. @observer
  2. class Detail extends Component {
  3. @observable info = {};
  4. @observable index = 1;
  5. @computed get sum() {
  6. return index * 4
  7. }
  8. @action
  9. onInfo = data => {
  10. this.info = data;
  11. };
  12. updateInfo = () => {
  13. this.onInfo(info) // 改变store属性
  14. }
  15. render() {
  16. return (
  17. <div>
  18. {this.info.name} // 使用里面的属性
  19. <Button onClick={this.updateInfo}>改变info</Button>
  20. </div>
  21. );
  22. }
  23. }

最后一种是我自己项目使用的,个人觉得不错。可以分离逻辑层和视图层。如果想使用全局stores,直接用inject导入第一种方式注入的stores。能较好划分全局stores单业务store的职责,而不是无脑的以树的方式全挂在index.js上面。子组件最好跟父组件用同一个store,方便沟通的同时层级不多也不会导致store太过复杂。

  1. // 逻辑层 用来处理业务逻辑
  2. import { observable, runInAction } from "mobx";
  3. import { aService, bService } from "../../../services/dispatch";
  4. import { T } from "react-toast-mobile";
  5. class ListStore {
  6. @observable list = [];
  7. serInitData = async () => {
  8. try {
  9. T.loading();
  10. const data = await aService()
  11. runInAction("serInitData", () => {
  12. this.list = data || [];
  13. });
  14. } catch (error) {
  15. T.notify(error.message);
  16. console.error(error);
  17. } finally {
  18. T.loaded();
  19. }
  20. };
  21. serBService = async vehicleNum => {
  22. try {
  23. T.loading();
  24. await bService(vehicleNum);
  25. runInAction("serBService", () => {
  26. this.list = this.list.filter(params => {
  27. return params.vehicleNum != vehicleNum;
  28. });
  29. });
  30. } catch (error) {
  31. T.notify(error.message);
  32. // eslint-disable-next-line no-console
  33. console.error(error);
  34. } finally {
  35. T.loaded();
  36. }
  37. };
  38. }
  39. export default ListStore;
  40. // 视图层 纯粹的视图展示和操作触发
  41. @observer
  42. class Detail extends Component {
  43. store = new ListStore();
  44. componentDidMount() {
  45. // 初始化数据
  46. this.store.serInitData();
  47. }
  48. updateInfo = () => {
  49. this.store.serBService(); // 调用bService
  50. }
  51. render() {
  52. return (
  53. <div>
  54. {this.store.list.map((item)=>{
  55. return
  56. <Provider myStore={this.store} >
  57. <Item key={item.guid} />
  58. </Provider>
  59. })} // 轮询列表
  60. <Button onClick={this.updateInfo}>改变info</Button>
  61. </div>
  62. );
  63. }
  64. }
  65. const Item = inject(allStores => ({
  66. aStore: all.aStore, // 连接到aStore
  67. myStore: allStores.myStore, // 连接到父组件的myStore
  68. }))(
  69. observer(function(props) { // 使用function方式在发送action等操作时,会带上Item这个组件名字,能变相的看到发送的来源。使用箭头函数则不会。而且react推荐的无状态组件也是使用的function
  70. return (
  71. <div>
  72. {this.props.aStore.xxxxx} // 使用全局store
  73. {this.props.myStore.xxxxx} // 使用父组件的store
  74. </div>
  75. );
  76. }));
一些备注
  1. 使用@inject后,无法通过组件的refs属性调用其对应的方法?
  1. if (this.tabIndex == "0") {
  2. // 加了@inject+@observer
  3. this.biddingRef.current.wrappedInstance.openFilter();
  4. } else if (this.tabIndex == "1") {
  5. // 只有@observer
  6. this.zdRef.current.openFilter();
  7. }
  8. 参考链接
  9. // https://stackoverflow.com/questions/43847401/reactnative-mobx-how-to-access-component-refs-from-mobx
  1. mobx如何自动保存数据
  1. import { observable, action, autorun, toJS, set } from "mobx";
  2. function autoSave(store, save) {
  3. let firstRun = true;
  4. autorun(() => {
  5. // 此代码将在每次运行任何可观察属性时运行
  6. // 对store进行更新。
  7. const json = JSON.stringify(toJS(store));
  8. if (!firstRun) {
  9. save(json);
  10. }
  11. firstRun = false;
  12. });
  13. }
  14. class RouteState {
  15. @observable state = {};
  16. constructor() {
  17. this.load();
  18. autoSave(this, this.save.bind(this));
  19. }
  20. load() {
  21. const storeTemp = sessionStorage.getItem("route_state");
  22. if (storeTemp) {
  23. const data = JSON.parse(storeTemp);
  24. set(this, data);
  25. }
  26. }
  27. save(json) {
  28. sessionStorage.setItem("route_state", json);
  29. }
  30. @action.bound
  31. actionState(_state) {
  32. this.state = _state;
  33. }
  34. }
  35. // 参考链接
  36. https://stackoverflow.com/questions/40292677/how-to-save-mobx-state-in-sessionstorage
场景模拟

列表A中有一个倒计时,这个倒计时会改变dataListitem里面的时间戳.

列表所在的页面有个筛选浮层,筛选浮层中有DatePicker组件

遇到的问题

DatePicker选择其他时间1s后,时间会重置为初始设置的时间.

原因

列表A中的倒计时每秒都在改变dataList,这导致该页面的子组件每秒都会执行render函数.导致DatePicker中的时间重置为初始时间.

解决办法

github iss

  1. // 新建一个包含组件.注意,这里不能用@observer将其包裹起来,
  2. // 包含组件应该是个干净的,由你控制是否重新渲染的组件
  3. class FilterPopContains extends Component {
  4. shouldComponentUpdate(nextProps, nextState) {
  5. // 在包含组件的shouldComponentUpdate方法中自己判断是否要执行render函数
  6. if (JSON.stringify(nextProps) != JSON.stringify(this.props)) return true;
  7. return false;
  8. }
  9. render() {
  10. // 将props原封不动传递给逻辑组件
  11. return <FilterPop {...this.props} />;
  12. }
  13. }
  14. // 这个是具体的内容组件,里面有你的逻辑
  15. @observer
  16. class BiddingFilterPop extends Component {
  17. render() {
  18. return <div>
  19. <DatePicker/>
  20. </div>
  21. }
  22. }
  1. mobx的使用非常灵活。可以多种使用方式在同一个项目使用。并不冲突。
  2. 推荐所有的组件都observer化,这并不会造成性能损耗,反而会优化组件。
  3. 想不到了。想起来再更新吧。

日常使用mobx的小技巧的更多相关文章

  1. 前端ps实用小技巧

    下面总结了几个日常使用PS的小技巧,希望对大家有所帮助(重点推荐第一个小技巧) 场景一:用ps测量PSD图中的元素宽高间距时,一般是手动使用 测量,但其实是有快捷键的,如下图 首先选中元素相应图层,然 ...

  2. ( 译、持续更新 ) JavaScript 上分小技巧(四)

    后续如有内容,本篇将会照常更新并排满15个知识点,以下是其他几篇译文的地址: 第一篇地址:( 译.持续更新 ) JavaScript 上分小技巧(一) 第二篇地址:( 译.持续更新 ) JavaScr ...

  3. ( 译、持续更新 ) JavaScript 上分小技巧(一)

    感谢好友破狼提供的这篇好文章,也感谢写这些知识点的作者们和将他们整理到一起的作者.这是github上的一篇文章,在这里本兽也就只做翻译,由于本兽英语水平和编程能力都不咋地,如有不好的地方也请多理解体谅 ...

  4. asp.net mvc route 中新发现的小技巧

    在发现这个小技巧之前,我经常被某些问题困扰,我们以博客园为例 1:是分类名称 2:是分类url 3:点击分类,进入的页面,要显示分类的名称 4:点击分类,进入的页面,要用分类相关参数 在日常web的开 ...

  5. Fiddler-010-网络延时应用小技巧-模拟低网速环境

    在日常的网络测试中,经常需要测试网络超时或在网络传输速率不佳的情况的应用场景,而与此同时我们有时手边资源有限,实现在各种真实网络(2G\3G)环境下测试有些局限性.其实 fiddler 已经提供了类似 ...

  6. 8 个 Git 的小技巧

    git 已经成为了我日常必备工具之一,我总结我几乎每天使用的8个有用(且简洁)的git技巧.   使用-p选择性添加 当你想提交内容时,你可以通过使用 git commit -am 来选择所有文件或使 ...

  7. Vim实用小技巧

    Vim实用小技巧 一些网络上质量较高的Vim资料 从我07年接触Vim以来,已经过去了8个年头,期间看过很多的Vim文章,我自己觉得非常不错,而且创作时间也比较近的文章有如下这些. Vim入门 目前为 ...

  8. 11个不常被提及的JavaScript小技巧

    这次我们主要来分享11个在日常教程中不常被提及的JavaScript小技巧,他们往往在我们的日常工作中经常出现,但是我们又很容易忽略. 1.过滤唯一值 Set类型是在 ES6中新增的,它类似于数组,但 ...

  9. 分享几个 SpringBoot 实用的小技巧

    前言 最近分享的一些源码.框架设计的东西.我发现大家热情不是特别高,想想大多数应该还是正儿八经写代码的居多:这次就分享一点接地气的: SpringBoot 使用中的一些小技巧. 算不上多高大上的东西, ...

随机推荐

  1. 《前端运维》二、Nginx--1基本概念及安装

    一.Nginx基本概念 简单来说,Nginx就是一个代理服务器,什么是代理服务器呢?也就是当我们访问服务器的时候,请求不会直接请求到服务器,中间会有个代理,代理会预先于服务器处理这些请求,最后由代理决 ...

  2. _u32定义

    驱动开发的原则: 能用__u32就最好用它,或者用u_int32_t之类的也可以,但不要直接用unsigned int等默认的数据类型.目的是让别人明白,你这个变量占多大内存. 原因: 1.你不能确定 ...

  3. MySQL SQL Injection Cheat Sheet

    MySQL SQL Injection Cheat Sheet Some useful syntax reminders for SQL Injection into MySQL databases- ...

  4. [转载]从phpinfo中能获取哪些敏感信息

    phpinfo()想必的最熟悉的了,在搭建环境之后都会随后写一个 phpinfo()来测试环境是否正常,很多人测试完毕忘记删除就开始部署环境了,这就造成了一些敏感信息的泄漏.那么我们能从 phpinf ...

  5. VS2022 安装.NET 3.5/.NET 4/.NET 4.5/.NET 4.5.1目标包的方法

    最近重装了系统,就装了一个Visual Studio 2022,发现之前的老项目打不开了,需要下载目标包,但是在Visual Studio Installer 里面无法安装 .NET 3.5/.NET ...

  6. grafana初级入门

    grafana初级入门 预备知识 Metrics.Tracing和Logging的区别 监控.链路追踪及日志作为实时监测系统运行状况,这三个领域都有对应的工具和解决方案. Metrics 监控指标的定 ...

  7. Spring Cloud与Spring Boot版本匹之间的关系

    由于学习的起步较晚,创建项目的时候一直采用的都是较新的springboot,用的2.0.2.RELEASE版本.参照网上的示例进行实验的时候,有时候会才坑,特记录一二以备忘 首先就是SpringBoo ...

  8. short s1 = 1; s1 = s1 + 1;有错吗?short s1 = 1; s1 += 1;有错吗?

    对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型.而short s1 = 1; s1 += 1;可 ...

  9. 构造器(constructor)是否可被重写(override)?

    构造器不能被继承,因此不能被重写,但可以被重载.

  10. spring-boot-learning 缓存之redis

    什么是BSD协议: BSD是"Berkeley Software Distribution"的缩写,意思是"伯克利软件发行版". BSD开源协议是一个给于使用者 ...