• Mobx的使用与版本区别


    Mobx

    Mobx是通过函数响应式编程使状态管理变得简单和可扩展的状态管理库。

    浏览器支持

    • image-20210805201938126
    • MobX 4 和 5 的 API 是相同的,并且语义上也能达到相同的效果,只是 MobX 4 存在一些 局限性

    一、使用 MobX 将一个应用变成响应式

    1. 定义状态并使其可观察

    可以用任何你喜欢的数据结构来存储状态,如对象、数组、类。 循环数据结构、引用,都没有关系。 只要确保所有会随时间流逝而改变的属性打上 mobx 的标记使它们变得可观察即可。

    import {observable} from 'mobx';
    
    var appState = observable({
        timer: 0
    });
    
    2. 创建视图以响应状态的变化

    appState 中相关数据发生改变时视图会自动更新。 MobX 会以一种最小限度的方式来更新视图。

    通常来说,任何函数都可以成为可以观察自身数据的响应式视图,MobX 可以在任何符合ES5JavaScript环境中应用。 但是在这所用的示例是 ES6版本的 React 组件视图。

    import {observer} from 'mobx-react';
    
    @observer
    class TimerView extends React.Component {
        render() {
            return (
                <button onClick={this.onReset.bind(this)}>
                    Seconds passed: {this.props.appState.timer}
                </button>
            );
        }
    
        onReset() {
            this.props.appState.resetTimer();
        }
    };
    
    ReactDOM.render(<TimerView appState={appState} />, document.body);
    
    3. 更改状态

    下面的代码每秒都会修改你的数据,而当需要的时候UI会自动更新。 无论是在改变状态的控制器函数中,还是在应该更新的视图中,都没有明确的关系定义。 使用 observable 来装饰你的状态视图,这足以让 MobX检测所有关系了。

    appState.resetTimer = action(function reset() {
        appState.timer = 0;
    });
    
    setInterval(action(function tick() {
        appState.timer += 1;
    }), 1000);
    

    只有在严格模式(默认是不启用)下使用 MobX 时才需要 action 包装。 建议使用 action,因为它将帮助你更好地组织应用,并表达出一个函数修改状态的意图。

    二、概念

    MobX 区分了以下几个应用中的概念。 在之前的要点中已经见过了,现在让我们更深入地了解它们。

    1. State(状态)

    状态 是驱动应用的数据。 通常有像待办事项列表这样的领域特定状态,还有像当前已选元素的视图状态。 记住,状态就像是有数据的excel表格。

    2. Derivations(衍生)

    任何 源自状态并且不会再有任何进一步的相互作用的东西就是衍生。 衍生以多种形式存在:

    • 用户界面
    • 衍生数据,比如剩下的待办事项的数量。
    • 后端集成,比如把变化发送到服务器端。

    MobX 区分了两种类型的衍生:

    • Computed values(计算值) - 从当前可观察状态中衍生出的值,即计算值是根据state推导计算出来的值;。
    • Reactions(反应) - 响应,受state影响,会对state的变化做出一些更新ui、打印日志等反应;

    黄金法则: 如果你想创建一个基于当前状态的值时,请使用 computed

    3. Actions(动作)

    动作 是任一一段可以改变状态的代码。用户事件、后端数据推送、预定事件、等等。 动作类似于用户在excel单元格中输入一个新的值。

    在 MobX 中可以显式地定义动作,它可以帮你把代码组织的更清晰。 如果是在严格模式下使用 MobX的话,MobX 会强制只有在动作之中才可以修改状态。

    三、原则

    MobX 支持单向数据流,也就是动作改变状态,而状态的改变会更新所有受影响的视图

    Action, State, View

    状态改变时,所有衍生都会进行原子级的自动更新。因此永远不可能观察到中间值。

    所有衍生默认都是同步更新。这意味着例如动作可以在改变状态之后直接可以安全地检查计算值。

    计算值延迟更新的。任何不在使用状态的计算值将不会更新,直到需要它进行副作用(I / O)操作时。 如果视图不再使用,那么它会自动被垃圾回收。

    所有的计算值都应该是纯净的。它们不应该用来改变状态

    四、实例

    下面的代码清单举例说明了以上的概念和原则:

    import {observable, autorun} from 'mobx';
    
    var todoStore = observable({
        /* 一些观察的状态 */
        todos: [],
    
        /* 推导值 */
        get completedCount() {
            return this.todos.filter(todo => todo.completed).length;
        }
    });
    
    /* 观察状态改变的函数 */
    autorun(function() {
        console.log("Completed %d of %d items",
            todoStore.completedCount,
            todoStore.todos.length
        );
    });
    
    /* ..以及一些改变状态的动作 */
    todoStore.todos[0] = {
        title: "Take a walk",
        completed: false
    };
    // -> 同步打印 'Completed 0 of 1 items'
    
    todoStore.todos[0].completed = true;
    // -> 同步打印 'Completed 1 of 1 items'
    Copy
    

    五、装饰器

    修饰器是一个对类进行处理的函数。

    基本语法

    @testable
    class MyTestableClass {
      // ...
    }
    
    function testable(target) {
      target.isTestable = true;
    }
    
    MyTestableClass.isTestable // true
    

    返回函数为修饰器传参

    function testable(isTestable) {
      return function(target) {
        target.isTestable = isTestable;
      }
    }
    
    @testable(true)
    class MyTestableClass {}
    MyTestableClass.isTestable // true
    
    @testable(false)
    class MyClass {}
    MyClass.isTestable // false
    

    添加实例属性

    function testable(target) {
      target.prototype.isTestable = true;
    }
    
    @testable
    class MyTestableClass {}
    
    let obj = new MyTestableClass();
    obj.isTestable // true
    

    实例:mixins

    // mixins.js
    export function mixins(...list) {
      return function (target) {
        Object.assign(target.prototype, ...list)
      }
    }
    
    // main.js
    import { mixins } from './mixins'
    
    const Foo = {
      foo() { console.log('foo') }
    };
    
    @mixins(Foo)
    class MyClass {}
    
    let obj = new MyClass();
    obj.foo() // 'foo'
    

    属性的修饰

    属性修饰器的参数:

    • 第一个参数是类的原型对象,上例是Person.prototype修饰器的本意是要“修饰”类的实例,但是这个时候实例还没生成,所以只能去修饰原型(这不同于类的修饰,那种情况时target参数指的是类本身)

    • 第二个参数是所要修饰的属性名

    • 第三个参数是该属性的描述对象

    更多:https://segmentfault.com/a/1190000013051904

    六、Mobx:observable

    什么是 Observable?

    Observable 是一种让数据的变化可以被观察的方法。

    哪些数据可被观察?

    • 原始类型

    • 对象

    • 数组

    • 。。。

    标记observable的三种方式总结

    1: 使用@observable

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

    2: 使用observable()

    import { observable } from "mobx"
    let person = observable({name: 'emma', weight: 50})
    

    3: 使用decorate()

    import { decorate, observable, computed } from "mobx"
    
    class Person {
        name = "John"
        age = 42
        showAge = false
    
        get labelText() {
            return this.showAge ? `${this.name} (age: ${this.age})` : this.name;
        }
    
        setAge(age) {
            this.age = age;
        }
    }
    // 使用 decorate 时,所有字段都应该指定
    decorate(Person, {
        name: observable,
        age: observable,
        showAge: observable,
        labelText: computed,
        setAge: action
    })
    

    七、Mobx:对可观察的数据做出响应

    • computed

    • autorun

      • 语法
      • 一上来就执行一次
      • 修改 autorun 中依赖的任意可观察数据都会导致aoturun的自动执行
      • computed 数据也可以被观察
    • when

      • 语法:第一个参数是一个函数,改函数返回一个条件布尔值,布尔值为 true,指定第二个函数,布尔值为 false,则不执行,而且保证只执行一次
      • 注意:必须根据可观察数据计算条件
      • 执行机制
    • reaction

      • 使用场景:在没有数据之前,不需要写缓存,可以利用 reaction 在数据第一次被填充之后执行写缓存的逻辑

    computed

    如果使用了@computed get getValue,那么getValue将会被缓存,如果value没有改变,那么getValue也不会改变,其它组件也不会收到通知。
    此外如果读取getValue的值,通常会得到一个缓存的值,而不带@computed装饰器,则会重新计算……

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

    引入computed的4种方式

    1:使用@computed

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

    2: 使用computed

    import {observable, computed} from 'mobx'
    
    let numbers = observable([1, 2, 3])
    let sum = computed(()=>numbers.reduce((a, b)=>a + b, 0))
    

    3: 使用decorate

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

    4:默认是computed的情况

    observable.object和extendObservable会自动把getter属性标记为computed:

    import { observable, computed, decorate } from "mobx"
    const OrderLine = observable.object({
      price: 0,
      amount: 1,
      get total(){
        return this.price * this.amount;
      }
    })
    

    autorun

    当使用 autorun 时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时会再次被触发。 相比之

    下,computed(function) 创建的函数只有当它有自己的观察者时才会重新计算,否则它的值会被认为是不相关的。

    经验法则:如果你有一个函数应该自动运行,但不会产生一个新的值,请使用autorun。 其余情况都应该使用 computed

    class store{
    	@observable count = 0;
    	@observable foo ='bar'
    }
    
    const store = new Store();
    
    autorun(() => {
      console.log('autorun:',store.count)
    })
    
    store.count = 10
    store.foo = 'hello'
    //执行结果如下:
    autorun: 0 
    autorun:10 
    

    observable可以用来观测一个数据,这个数据可以数字、字符串、数组、对象等类型,而当观测到的数据发生变化的时候,如果变化的值处在autorun中,那么autorun就会自动执行。

    上例中的autorun函数中,只对count值进行了操作,所以store.foo = 'hello’这步并不会触发autorun,只有count的变化才触发了autorun。

    class store{
    	@observable count = 0;
    	@observable foo ='bar'
    }
    
    const store = new Store();
    
    autorun(() => {
      console.log('autorun:',store.count,store.foo)
    })
    
    store.count = 10
    store.foo = 'hello'
    //执行结果如下:
    autorun: 0 bar
    autorun:10 bar
    autorun: 10 hello
    

    频繁执行autorun,数据量大时不合理

    解决方法:

    class store{
    	@observable count = 0;
    	@observable foo ='bar'
      @action change() {
        this.count = 10;
        this.foo = 'hello'
      }
    }
    const store = new Store();
    
    autorun(() => {
      console.log('autorun:',store.count,store.foo)
    })
    
    store.change()
    //执行结果如下:
    autorun: 0 bar
    autorun: 10 hello
    

    强制必须使用action装饰的函数修改observable的值

    import { configure } from 'mobx';
    configure({
      enforceActions: 'observed'
    })
    

    when

    when(predicate: () => boolean, effect?: () => void, options?)
    

    when 观察并运行给定的 predicate,直到返回true。 一旦返回 true,给定的 effect 就会被执行,然后 autorunner(自动运行程序) 会被清理。 该函数返回一个清理器以提前取消自动运行程序。

    对于以响应式方式来进行处理或者取消,此函数非常有用。 示例:

    class MyResource {
        constructor() {
            when(
                // 一旦...
                () => !this.isVisible,
                // ... 然后
                () => this.dispose()
            );
        }
    
        @computed get isVisible() {
            // 标识此项是否可见
        }
    
        dispose() {
            // 清理
        }
    }
    Copy
    
    class store{
    	@observable count = 0;
    }
    const store = new Store();
    //当count>100时,只执行一次自定义逻辑
    when(
      () => {
        return store.count > 100;
      },
      () => {
        console.log("when:",store.count);
      }
    )
    

    reaction

    用法: reaction(() => data, (data, reaction) => { sideEffect }, options?)

    autorun 的变种,对于如何追踪 observable 赋予了更细粒度的控制。 它接收两个函数参数,第一个(数据 函数)是用来追踪并返回数据作为第二个函数(效果 函数)的输入。 不同于 autorun 的是当创建时效果 函数不会直接运行,只有在数据表达式首次返回一个新值后才会运行。 在执行 效果 函数时访问的任何 observable 都不会被追踪。

    reaction 返回一个清理函数。

    传入 reaction 的第二个函数(副作用函数)当调用时会接收两个参数。 第一个参数是由 data 函数返回的值。 第二个参数是当前的 reaction,可以用来在执行期间清理 reaction

    不同于autorun和when,只有当被观测的数据发生改变时才执行,先执行第一个函数,把第一个函数返回的结果传给第二个函数,data为第一个函数的返回值。第二个参数reaction表示当前函数本身。

    reaction(
    	() => {
        //执行一些业务逻辑操作,返回数据给下一个函数使用
        return store.count;
      },
      (data, reaction) => {
        console.log("data:",data)//手动停止当前reaction的监听
        reaction.dispose();
      }
    )
    

    八、Mobx:改变 observables

    Action

    • action

      mobx推荐将修改被观测变量的行为放在action中。

      例子:

      import {observable, action} from 'mobx';
      class Store {
        @observable number = 0;
        @action add = () => {
          this.number++;
        }
      }
      
      const newStore = new Store();
      newStore.add();
      

      好了回到我们的例子,这个类中有一个add函数,用来将number的值加1,也就是修改了被观测的变量,根据规范,我们要在这里使用action来修饰这个add函数。

      就算我把@action去掉,程序还是可以运行。

      class Store {
        @observable number = 0;
        add = () => {
          this.number++;
        }
      }
      

      这是因为现在我们使用的Mobx的非严格模式,如果在严格模式下,就会报错了。
      接下来让我们来启用严格模式

      import {observable, action, useStrict} from 'mobx';
      useStrict(true);
      class Store {
        @observable number = 0;
        @action add = () => {
          this.number++;
        }
      }
      
      const newStore = new Store();
      newStore.add();
      

      Mobx里启用严格模式的函数是useStrict,注意和原生JS的"use strict"不是一个东西。
      现在再去掉@action就会报错了。
      实际开发的时候建议开起严格模式,这样不至于让你在各个地方很轻易地区改变你所需要的值,降低不确定性。

    • action.bound

      action.bound 可以用来自动地将动作绑定到目标对象。,保证this永远指向容器的实例对象

      class store{
      	@observable count = 0;
      	@observable foo ='bar'
        @action.bound change() {
          console.log(this)
        }
      }
      const store = new Store();
      
      autorun(() => {
        console.log('autorun:',store.count,store.foo)
      })
      
      const change = store.change;
      change();
      //执行结果如下:
      Store{change: f, Symbol(mobx did run ...)}
      //去掉.bound后执行结果
      undefined
      

      注意: *action.bound* 不要和箭头函数一起使用;箭头函数已经是绑定过的并且不能重新绑定。

    • runInAction

      它接收代码块并在(异步的)动作中执行。这对于即时创建和执行动作非常有用,例如在异步过程中。runInAction(f)action(f)() 的语法糖。

      class store{
      	@observable count = 0;
      	@observable foo ='bar'
        @action.bound change() {
          console.log(this)
        }
      }
      const store = new Store();
      
      autorun(() => {
        console.log('autorun:',store.count,store.foo)
      })
      
      runInAction(() => {
        store.count = 10;
        store.foo = 'hello'
      })
      //执行结果如下:
      autorun: 0 bar
      autorun: 10 hello
      
    • 异步action

    • action只能影响正在运行的函数,而无法影响当前函数调用的异步操作

      开启action严格模式后,不允许在异步的回调函数中对容器状态进行修改。

      class store{
      	@observable count = 0;
      	@observable foo ='bar'
        @action.bound change() {
          console.log(this)
        }
      	/*错误用法
      	@action.bound asyncChange() {
          setTimeout(() => {
            this.count = 100;
          },100)
        }
        */
      	//方法一
      	@action.bound asyncChange() {
          setTimeout(() => {
            this.changeCount();
          },100)
        }
      	@action.bound changeCount() {
          this.count = 20;
        }
        //方法二
      	@action.bound asyncChange() {
          setTimeout(() => {
            //定义一个名称为changeFoo的action函数并立即执行
            action('changeFoo',() => {
              this.foo = 'hello'
            })();
          },100)
        }
      	//方法三
      	@action.bound asyncChange() {
          setTimeout(() => {
            //定义一个名称为changeFoo的action函数并立即执行
            runInAction(() => {
              this.foo = 'hello'
            });
          },100)
        }
      }
      const store = new Store();
      store.asyncChange();
      

    Mobx官网实例:

    @action createRandomContact() {
      this.pendingRequestCount++;
      superagent
        .get('https://randomuser.me/api/')
        .set('Accept', 'application/json')
        .end(action("createRandomContact-callback", (error, results) => {
          if (error)
            console.error(error);
          else {
            const data = JSON.parse(results.text).results[0];
            const contact = new Contact(this, data.dob, data.name, data.login.username, data.picture);
            contact.addTag('random-user');
            this.contacts.push(contact);
            this.pendingRequestCount--;
          }
      }));
    }
    

    重点关注程序的第六行。在end中触发的回调函数,被action给包裹了,这就很好验证了上面加粗的那句话,action无法影响当前函数调用的异步操作,而这个回调毫无疑问是一个异步操作,所以必须再用一个action来包裹住它,这样程序才不会报错。。

    使用async function来处理业务,那么我们可以使用runInAction这个API来解决之前的问题。

    import {observable, action, useStrict, runInAction} from 'mobx';
    useStrict(true);
    
    class Store {
       name = '';
       load = async () => {
        const data = await getData();
        runInAction(() => {
          this.name = data.name;
        });
      }
    }
    

    九、结合React使用

    在React中,我们一般会把和页面相关的数据放到state中,在需要改变这些数据的时候,我们会去用setState这个方法来进行改变。

    先设想一个最简单的场景,页面上有个数字0和一个按钮。点击按钮我要让这个数字增加1,就让我们要用Mobx来处理这个试试。

    import React from 'react';
    import { observable, useStrict, action } from 'mobx';
    import { observer } from 'mobx-react';
    useStrict(true);
    
    class MyState {
      @observable num = 0;
      @action addNum = () => {
        this.num++;
      };
    }
    
    const newState = new MyState();
    
    @observer
    export default class App extends React.Component {
    
      render() {
        return (
          

    {newState.num}

    ) } }

    上例中我们使用了一个MyState类,在这个类中定义了一个被观测的num变量和一个action函数addNum来改变这个num值。

    之后我们实例化一个对象,叫做newState,之后在我的React组件中,我只需要用@observer修饰一下组件类,便可以使用这个

    newState对象中的值和函数了。

    跨组件交互

    在不使用其它框架、类库的情况下,React要实现跨组件交互这一功能相对有些繁琐。通常我们需要在父组件上定义一个state和一个修改该state的函数。然后把state和这个函数分别传到两个子组件里,在逻辑简单,且子组件很少的时候可能还好,但当业务复杂起来后,这么写就非常繁琐,且难以维护。而用Mobx就可以很好地解决这个问题。来看看以下的例子:

    class MyState {
      @observable num1 = 0;
      @observable num2 = 100;
    
      @action addNum1 = () => {
        this.num1 ++;
      };
      @action addNum2 = () => {
        this.num2 ++;
      };
      @computed get total() {
        return this.num1 + this.num2;
      }
    }
    
    const newState = new MyState();
    
    const AllNum = observer((props) => 
    num1 + num2 = {props.store.total}
    ); const Main = observer((props) => (

    num1 = {props.store.num1}

    num2 = {props.store.num2}

    )); @observer export default class App extends React.Component { render() { return (
    ); } }

    有两个子组件,Main和AllNum (均采用无状态函数的方式声明的组件)

    在MyState中存放了这些组件要用到的所有状态和函数。

    之后只要在父组件需要的地方实例化一个MyState对象,需要用到数据的子组件,只需要将这个实例化的对象通过props传下去就好了。

    那如果组件树比较深, 则可以借助React15版本的新特性context来完成。它可以将父组件中的值传递到任意层级深度的子组件中。

    网络请求
    useStrict(true);
    
    class MyState {
      @observable data = null;
      @action initData = async() => {
        const data = await getData("xxx");
        runInAction("说明一下这个action是干什么的。不写也可以", () => {
          this.data = data;
        })
      };
    }
    

    严格模式下,只能在action中修改数据,但是action只能影响到函数当前状态下的情景,也就是说在await之后发生的事情,这个action就修饰不到了,于是我们必须要使用了runInAction。

    开启严格模式可以防止数据被任意修改,降低程序的不确定性*

    React组件中可以直接添加@observable修饰的变量

    @observer
    class MyComponent extends React.Component {
      
      state = { a: 0 };
    
      @observable b = 1;
    
      render() {
        return(
          <div>
           {this.state.a}
           {this.b}
          </div>
        )
      }
    }
    

    在添加@observer后,你的组件会多一个生命周期componentWillReact。当组件内被observable观测的数据改变后,就会触发这个生命周期。

    注意setState并不会触发这个生命周期!state中的数据和observable数据并不算是一类。

    另外被observable观测数据的修改是同步的,不像setState那样是异步,这点给我们带了很大便利

    十、Observable Object和Observable Arrays

    Observable Objects

    如果使用observable来修饰一个Javascript的简单对象,那么其中的所有属性都将变为可观察的,如果其中某个属性是对象或者数组,那么这个属性也将被observable进行观察(递归调用)。

    Tips: 简单对象是指不由构造函数创建,而是使用Object作为其原型,或是干脆没有原型的对象。

    需要注意,只有对象上已经存在的属性,才能被observable所观测到。

    若是当时不存在,后续添加的属性值,则需要使用extendObservable来进行添加。

    let observableObject = observable({value: 3222});
    
    extendObservable(observableObject, {
      newValue: 2333
    });
    

    如果是由构造函数创建的对象,那么必须要再它的构造函数中使用observable或extendObservable来观测对象。

    如下所示:

    function MyObject(name) {
      extendObservable(this, {
        name,
      });
    }
    
    var obj = new MyObject("aaa");
    

    如果对象中的属性是由构造函数创建的对象,那么它也不会被observable给转化。

    对象中带有getter修饰的属性会被computed自动转换。

    Observable Arrays

    与对象类似,数组同样可以使用observable函数进行转化。

    考虑到ES5中原生数组对象中存在一定的限制,所以Mobx将会创建一个类数组对象来代替原始数组。在实际使用中,这些类数组的表现和真正的原生数组极其类似,并且它支持原生数组的所有API,包括数组索引、长度获取等。

    但是注意一点,sort和reverse方法返回的是一个新的Observable Arrays,对原本的类数组不会产生影响,这一点和原生数组不一样。

    请记住,这个类数组不管和真实的数组有多么相似,它都不是一个真正的原生数组,所以毫无疑问Array.isArray(observable([]))的返回值都是false。

    当你需要将这个Observable Arrays转换成真正的数组时,可以使用slice方法创建一个浅拷贝。换句话来说,Array.isArray(observable([]).slice())会返回true。

    除了原生数组支持的API外,Observable Arrays还支持以下API:

    • intercept(interceptor)
      这个方法可以在所有数组的操作被应用之前,将操作拦截。
    • observe(listener, fireImmediately? = false)
      用来监听数组的变化(类似ES7中的observe,可惜这个ES7中的observe将被废弃),它返回一个用以注销监听器的函数。
    • clear()
      清空数组
    • replace(newArray)
      用一个新数组中的内容来替换掉原有的内容
    • find(predicate: (item, index, array) => boolean, thisArg?, fromIndex?)
      基本上与ES7 Array.find的提案相同,不过多了fromIndex参数。
    • remove(value)
      移除数组中第一个值等于value的元素,如果移除成功,则会返回true
    • peek()
      和slice类似,但它不会创建保护性拷贝,所以性能比slice会更好。如果你能够确定,转换出的数组肯定仅以只读的方式使用,那么可以使用这个API

    十一、Mobx 4/5 与Mobx 6 的区别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pa9CT6mO-1664247481872)(https://sponsor.segmentfault.com/lg.php?bannerid=0&campaignid=0&zoneid=25&loc=https%3A%2F%2Fsegmentfault.com%2Fa%2F1190000037619304&referer=https%3A%2F%2Fsegmentfault.com%2Fsearch%3Fq%3Dmobx%26page%3D1&cb=032bde81ac)]

    create-react-app搭建项目,安装了mobx@6.xmobx-react@6.x,写一个使用store例子时发现依赖store的组件没有重新渲染。

    示例:

    // mobx@6.0.1
    import { action, observable } from 'mobx';
    
    class TestStore {
      @observable count = 0;
    
      @action
      setValue = <T extends keyof CountStore>(key: T, value: this[T]) => {
        this[key] = value;
      }
    }
    
    export default {
      testStore: new TestStore()
    }
    

    页面引入

    import { inject, observer } from 'mobx-react';
    import React from 'react';
    
    enum Oprate {
      MINUS = 'MINUS',
      PLUS = 'PLUS'
    }
    
    function App(props: any) {
    
      const {testStore} = props;
    
      const oprate = (type: Oprate) => {
        switch (type) {
          case Oprate.MINUS:
            testStore.setValue('count', testStore.count - 1);
            break;
          case Oprate.PLUS:
            testStore.setValue('count', testStore.count + 1);
            break;
          default:
            break;
        }
      }
    
      return (
        <div>
          <button onClick={() => oprate(Oprate.MINUS)}>--</button>
          <span>{testStore?.count}</span>
          <button onClick={() => oprate(Oprate.PLUS)}>++</button>
        </div>
      );
    }
    
    export default inject('testStore')(observer(App));
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKb8n9Me-1664247481873)(https://segmentfault.com/img/bVcH0nD)]

    Mobx官网例子如下:

    import React from "react"
    import ReactDOM from "react-dom"
    import { makeAutoObservable } from "mobx"
    import { observer } from "mobx-react"
    
    // Model the application state.
    class Timer {
        secondsPassed = 0
    
        constructor() {
            makeAutoObservable(this)
        }
    
        increase() {
            this.secondsPassed += 1
        }
    
        reset() {
            this.secondsPassed = 0
        }
    }
    
    const myTimer = new Timer()
    
    // Build a "user interface" that uses the observable state.
    const TimerView = observer(({ timer }) => (
        <button onClick={() => timer.reset()}>Seconds passed: {timer.secondsPassed}</button>
    ))
    
    ReactDOM.render(<TimerView timer={myTimer} />, document.body)
    
    // Update the 'Seconds passed: X' text every second.
    setInterval(() => {
        myTimer.increase()
    }, 1000)
    

    无需通过observableaction等修饰器,直接在构造函数中使用makeAutoObservable来实现observableaction修饰器功能,使代码更加简洁。

    将上面例子改写一下就可以了

    import { makeAutoObservable  } from 'mobx';
    
    class TestStore {
    
      constructor() {
        makeAutoObservable(this);
      }
      /*
      constructor() {
        makeObservable(this, {
         count: observable,
         setvalue: action,
       });
       */
    
      count = 0;
    
      setValue = <T extends keyof CountStore>(key: T, value: this[T]) => {
        this[key] = value;
      }
    
    }
    
    export default {
        testStore: new TestStore()
    }
    
  • 相关阅读:
    2475. 数组中不等三元组的数目-快速排序+遍历求和
    SpringCloud学习笔记(四)
    (转帖)微服务拆分的原则和方法(2)
    微信网页版登录插件v1.1.1
    专精特新新企业技术创新发展趋势研究分析讲座详情
    【漏洞分析】KaoyaSwap 安全事件分析
    黑产工具情报的分析方式浅析
    李航老师《统计学习方法》第四章阅读笔记
    代码随想录算法训练营DAY36|C++贪心算法Part.5|435.无重叠区间、763.划分字母区间、56. 合并区间
    多维数组(二维数组)
  • 原文地址:https://blog.csdn.net/Sponge_bob_/article/details/127067654