强大的状态管理工具-Mobx

2023-12-18 17:38

本文主要是介绍强大的状态管理工具-Mobx,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

Mobx是一款精准的状态管理工具库,如果你在 React 和 React Native 应用中使用过 Redux ,那毫不犹豫地说,MobX 的简单性将成为你状态管理的不二之选,本文主要讲的是关于Mobx在React-native下的使用和常见问题。

常见API

在使用Mobx之前,首先介绍几个常见的API

1. observable

Mobx如此简单的原因之一,就是使用了可观察数据(observable Data),简单来说,可观察数据就是可以观察到数据的读取,写入,并进行拦截
Mobx中提供了observable接口来定义可观察数据,可观察数据的类型可以使基本数据类型,object,array或者ES6中的Map类型,
注意:数组经过observable包装之后,就不是Array类型了,而是Mobx中的一个特殊类型,observable类型。
虽然数据类型不一样,但是使用方式和原来使用方式一致(原始数据类型除外)

const Array =  observable([1,2,3]);
const object =  observable({name: 'Jay'});
const Map =  observable(new Map([['name','ding']]));console.log(Array[0])  // 1
console.log(object.name)  // Jay
console.log(Map.get('name'))  // ding

@observable

装饰器可以再ES7或者TypeScript类属性中使用,将其转换成可观察的。 @observable可以再字段和属性getter上使用,对于对象的哪部分需要成为可观察对象,@observable 提供了细粒度的控制。

import { observable, computed } from "mobx";class Order {@observable price = 0;@observable amount = 1;@computed get total() {return this.price * this.amount;}
}

observer

observer接收一个React-native组件作为参数,并将其转换成响应式组件


@observer export default class App extends Component  {render() {return (<View></View>)}
}

响应式组件,即当且仅当组件依赖的可观察对象数据发生改变时,组件才会自动相应并且重新渲染,而在传统的react-native应用中,当状态属性变化后会先调用shouldComponentUpdate,该方法会深层对比前后状态和属性是否发生改变,再确定是否更新组件。

shouldComponentUpdate是很消耗性能的,Mobx通过可观察数据,精确地知道组件是否需要更新,减少了利用shouldComponentUpdate这个方法,这是Mobx性能好的原因之一
陷阱:Mobx可以做很多事,但是它还是无法将原始数据类型转换成可观察的,所以值是不可观察的,但是对象的属性可以被观察,这意味着 @observer 实际上是对间接引用(dereference)值的反应。

computed

计算值(computed values)是可以根据现有状态或其它计算值衍生出的值,计算的耗费是不可低估的,computed尽可能帮你减少其中的耗费,它们是高度优化的。
computed values是自动帮你从你的状态(state)值和其他计算辅助值来计算的。MobX做了很多的优化。当参与计算的值没有发生改变,Computed是不会重新运行。如果参与计算的值没有被使用,Computed values是暂停的。如果Computed values不再是观察者(observed),那么在UI上也会把它除掉,MobX能自动做垃圾回收,用法如下:

class foo {@observable length: 2,@computed get squared() {return this.length * this.length;}
}

Autorun

Autorun是用在一些你想要产生一个不用观察者参与的被动调用函数里面。当autorun被使用的时候,一旦依赖项发生变化,autorun提供的函数就会被执行。与之相反的是,computed提供的函数只会在他有自己的观察员(observers)的时候才会评估是否重新执行,否则它的值被认为是无用的
综上所述:如果你需要一个自动运行但确不会产生任何新的值的结果的函数,就可以使用Autorun,其他情况可以使用computed,Autorun只是作用于如果达到某个效果或者功能,而不是计算某些值
就像 @ observer 装饰器/函数,autorun 只会观察在执行提供的函数时所使用的数据。

var numbers = observable([1,2,3]);
var sum = computed(() => numbers.reduce((a, b) => a + b, 0));
var disposer = autorun(() => console.log(sum.get()));
// 输出 '6'
numbers.push(4);
// 输出 '10'disposer();
numbers.push(5);
// 不会再输出任何值。`sum` 不会再重新计算

action

  • 任何应用程序都有操作(action),action是任何改变状态的事物,使用Mobx
    ,可以通过标记他们在你的代码中显式的显示你的操作(action),它会更好的组织你的代码,它们用于修改可观察量或具有副作用的任何函数中。
    需要注意的是:action是用在strict mode 中的

    用法:

  • action(fn)
  • action(name, fn)
  • @action classMethod() {}
  • @action(name) classMethod () {}
  • @action boundClassMethod = (args) => { body }
  • @action(name) boundClassMethod = (args) => { body }
  • @action.bound classMethod() {}
  // 添加图片@action addImg = () => {ImagePicker.openPicker({multiple: true,waitAnimationEnd: false,includeExif: true,forceJpg: true,maxFiles: 9 - this.imgs.length,compressImageQuality: 0.5,}).then((images) => {console.log(images)}).catch((err) => {console.warn(err)})}

2.Action仅仅作用于当前运行的函数,而不是作用于当前函数调用的函数,这意味着在一些定时器或者网络请求,异步处理的情况下,它们的回调函数无法对状态改变,这些回调函数都应该包裹在action里面,但是,如果你使用了async / await的话,最好的方式应该是使用 runInAction 来让它变得更加简单

@action /*optional*/ updateDocument = async () => {const data = await fetchDataFromUrl();/* required in strict mode to be allowed to update state: */runInAction("update state after fetching data", () => {this.data.replace(data);this.isSaving = true;})
}

常见可观察类型

Observable 对象

observable.object方法将对象变为可观察的,它实际上是把对象的所有属性转换为可观察的,存放到一个代理对象上,以减少对原对象的污染,默认情况下,observable是递归应用的,所以如果对象的某个值是一个对象或数组,那么该值也将通过 observable 传递

import {observable, autorun, action} from "mobx"var person = observable({name : 'jack',age:24,sex:'男'
})

Observable 数组

与对象类似,可以使用Observable.array(array)或者将数组传给 observable,可以将数组转换成可观察的,这也是递归的,所以数组中的所有(未来的)值都会是可观察的。

import {observable, autorun} from "mobx";
var todos = observable([{ title: "Spoil tea", completed: true },{ title: "Make coffee", completed: false }
]);
autorun(() => {console.log("Remaining:", todos.filter(todo => !todo.completed).map(todo => todo.title).join(", "));
});
// 输出: 'Remaining: Make coffee'todos[0].completed = false;
// 输出: 'Remaining: Spoil tea, Make coffee'todos[2] = { title: 'Take a nap', completed: false };
// 输出: 'Remaining: Spoil tea, Make coffee, Take a nap'todos.shift();
// 输出: 'Remaining: Make coffee, Take a nap'

除了可以使用所有的内置函数,observable数组还提供了好多方法供我们使用

  • clear() -- 从数组中删除所有项
  • replace(newItems) -- 用新元素替换数组中所有已存在的元素
  • remove(value) -- 通过值从数组中移除一个单个的元素。

注意:

不同于sort和reverse函数的实现,observableArray.sort 和 observableArray.reverse 不会改变数组本身,而只是返回一个排序过/反转过的拷贝,在 MobX 5 及以上版本中会出现警告。推荐使用 array.slice().sort() 来替代。

Observable Map

与数组的处理方式类似,Mobx也实现了一个ObservableMap类,不过只支持字符串。数字或Bool值作为键,ObservableMap在可观察对象的基础上,还要使键的增删可观察。它可以看做两个可观察映射和一个可观察数组的组合:

import {observable, autorun} from "mobx"
const map = observable(new Map());autorun(() => {console.log(map.get('key'));
});map.set('key', 'value'); // 新增 key-value 键值对,输出 value
map.set('key', 'anotherValue'); // 修改为 key-anotherValue,输出 anotherValue
map.set('prop', 'value'); // 不输出
map.delete('prop'); // 不输出

优化React 组件

避免在父组件中访问子组件的属性

在文档中也有提到过这个问题,Mobx对于一个observer组件,是通过访问属性来访问以来的,所以哪怕父组件里没有用到这个属性,只是为了作为props传给子组件,Mobx还是会算它依赖了这个属性,于是会产生不必要的更新,最好的方式是把数据统一放到Store中,子组件通过 inject store 方式获取数据。

小组件

由于React的机制,Mobx只能在组件层发光发热,对于组件内部就是无能为力了,所以大组件很容易卡死,小组件才能真正发挥Mobx的优势。

在专用组件中渲染列表

React在渲染大型数据集合的时候处理的很不好,因为协调器必须评估每个集合变化的集合所产生的组件。因此,建议使用专门的组件来映射集合并渲染这个组件,且不再渲染其他组件:

官方提供的demo如下:

不妥的处理方式:

@observer class MyComponent extends Component {render() {const {todos, user} = this.props;return (<div>{user.name}<ul>{todos.map(todo => <TodoView todo={todo} key={todo.id} />)}</ul></div>)}
}

在示例中,当user.name发生变化时React 会不必要地协调所有的 TodoView 组件。尽管TodoView 组件不会重新渲染,但是协调的过程本身是非常昂贵的。

正确的处理方式:

@observer class MyComponent extends Component {render() {const {todos, user} = this.props;return (<div>{user.name}<TodosView todos={todos} /></div>)}
}@observer class TodosView extends Component {render() {const {todos} = this.props;return <ul>{todos.map(todo => <TodoView todo={todo} key={todo.id} />)}</ul>)}
}

在react-native使用Mobx常见的问题

observable 数组的类型是对象

observable 数组类型其实是个对象,所以它遵循propTypes.object ,如果使用propTypes.array 会报错。mobx-react 为 observable 数据结构提供了明确的 PropTypes。

在 React Native 中渲染 ListView

React Native 的的DataSource只能接收真正的数组,但是observable 数组是个对象,所以在传给ListView之前使用.slice方法,此外,ListView.DataSource 本身可以移到 store 之中并且使用 @computed 自动地更新,这步操作同样可以在组件层完成

class ListStore {@observable list = ['Hello World!','Hello React Native!','Hello MobX!'];ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });@computed get dataSource() {return this.ds.cloneWithRows(this.list.slice());}
}const listStore = new ListStore();@observer class List extends Component {render() {return (<ListViewdataSource={listStore.dataSource}renderRow={row => <Text>{row}</Text>}enableEmptySections={true}/>);}
}

原文http://techblog.sishuxuefu.com/atricle.html?5bb9f17a0b6160006f5988bb

这篇关于强大的状态管理工具-Mobx的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/509253

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

hdu1565(状态压缩)

本人第一道ac的状态压缩dp,这题的数据非常水,很容易过 题意:在n*n的矩阵中选数字使得不存在任意两个数字相邻,求最大值 解题思路: 一、因为在1<<20中有很多状态是无效的,所以第一步是选择有效状态,存到cnt[]数组中 二、dp[i][j]表示到第i行的状态cnt[j]所能得到的最大值,状态转移方程dp[i][j] = max(dp[i][j],dp[i-1][k]) ,其中k满足c

状态dp总结

zoj 3631  N 个数中选若干数和(只能选一次)<=M 的最大值 const int Max_N = 38 ;int a[1<<16] , b[1<<16] , x[Max_N] , e[Max_N] ;void GetNum(int g[] , int n , int s[] , int &m){ int i , j , t ;m = 0 ;for(i = 0 ;

hdu3006状态dp

给你n个集合。集合中均为数字且数字的范围在[1,m]内。m<=14。现在问用这些集合能组成多少个集合自己本身也算。 import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.Inp

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

实例:如何统计当前主机的连接状态和连接数

统计当前主机的连接状态和连接数 在 Linux 中,可使用 ss 命令来查看主机的网络连接状态。以下是统计当前主机连接状态和连接主机数量的具体操作。 1. 统计当前主机的连接状态 使用 ss 命令结合 grep、cut、sort 和 uniq 命令来统计当前主机的 TCP 连接状态。 ss -nta | grep -v '^State' | cut -d " " -f 1 | sort |

状态模式state

学习笔记,原文链接 https://refactoringguru.cn/design-patterns/state 在一个对象的内部状态变化时改变其行为, 使其看上去就像改变了自身所属的类一样。 在状态模式中,player.getState()获取的是player的当前状态,通常是一个实现了状态接口的对象。 onPlay()是状态模式中定义的一个方法,不同状态下(例如“正在播放”、“暂停

IntelliJ IDEA - 强大的编程工具

哪个编程工具让你的工作效率翻倍? 在日益繁忙的工作环境中,选择合适的编程工具已成为提升开发者工作效率的关键。不同的工具能够帮助我们简化代码编写、自动化任务、提升调试速度,甚至让团队协作更加顺畅。那么,哪款编程工具让你的工作效率翻倍?是智能的代码编辑器,强大的版本控制工具,还是那些让你事半功倍的自动化脚本?在这里我推荐一款好用的编程工具:IntelliJ IDEA。 方向一:工具介绍 Int

qml states 状态

states 状态 在QML中,states用于定义对象在不同状态下的属性变化。每个状态可以包含一组属性设置,当状态改变时,这些属性设置会被应用到对象上。 import QtQuick 2.15import QtQuick.Controls 2.15// 定义应用程序的主窗口ApplicationWindow {visible: true // 使窗口可见width: 640 /

[轻笔记] ubuntu Shell脚本实现监视指定进程的运行状态,并能在程序崩溃后重启动该程序

根据网上博客实现,发现只能监测进程离线,然后对其进行重启;然而,脚本无法打印程序正常状态的信息。自己通过不断修改测试,发现问题主要在重启程序的命令上(需要让重启的程序在后台运行,不然会影响监视脚本进程,使其无法正常工作)。具体程序如下: #!/bin/bashwhile [ 1 ] ; dosleep 3if [ $(ps -ef|grep exe_name|grep -v grep|