react的设计思想就是界面由数据驱动,公式:UI = f(data)。UI代表最终渲染的界面,f表示的是一个函数,data就是数据。data可以来自于组件内部的state,也可以是props。就是下文中讲到的内容。
一、组件状态state
组件内的数据分为两种
- state 组件内部数据,外部不可见
- props 外部传入的数据
所以,判断一个数据应该放在哪里,用下面的原则:
1、如果数据由外部传入,放在 props 中;
2、如果是组件内部状态,是否这个状态更改应该立刻引发一次组件重新渲染?如果是,放在 state 中;不是,放在成员变量中。
如何修改state:
1 this.state.foo = 'bar'; //错误的方式 2 this.setState({foo:'bar'}); //正确的方式
如上面代码所示,如果只是修改 this.state,那改了也就只是改了这个对象,其他的什么都不会发生;如果使用 setState 函数,那不光修改 state,还能引发组件的重新渲染,在重新渲染中就会使用修改后的 state,这也就是达到根据 state 改变公式左侧 UI 的目的。
但是setState并不是每次调用都能触发渲染。React为了优化渲染,避免每次setState都去渲染而浪费性能,用了任务队列的方式来解决,多次setState调用会往队列里放多个任务,然后React会选择合适的时机去处理,当批量处理开始时,会合并多个setState,比如下面:
1 this.setState({count: 1}); 2 this.setState({caption: 'foo'}); 3 this.setState({count: 2});
最终会被合并为this.setState({count: 2, caption: 'foo'});所以被合并后多次的setState只会更新一次,从而只引发一次重新渲染。当然也因为有这个队列的存在setState在React中无法保证同步更新state中的数据。注意:setState中的对象合并都是->浅合并
state的批处理:
可以这么理解,当 React 调用某个组件的生命周期函数或者事件处理函数时,React 会想:“嗯,这一次函数可能调用多次 setState,我会先打开一个标记,只要这个标记是打开的,所有的 setState 调用都是往任务队列里放任务,当这一次函数调用结束的时候,我再去批量处理任务队列,然后把这个标记关闭。”
(这里可以对应到pendingTime:https://react.jokcy.me/book/update/expiration-time.html)
那么如何进行setState的连续操作并产生同步效果?使用函数式setState
setState 的第一个参数为函数时,任务列表上增加的就是一个可执行的任务函数了,React 每处理完一个任务,都会更新 this.state,然后把新的 state 传递给这个任务函数。
1 function increment(state, props) { 2 return {count: state.count + 1}; 3 } 4 this.setState(increment); 5 this.setState(increment); 6 this.setState(increment);
二、Redux数据管理工具
Redux相当于是所有组件共享的数据store,打破了父子、子父、兄弟组件之间的数据传递的路径,都可以直接去store里拿。
适合redux的场景:
第一步,看这个状态是否会被多个 React 组件共享。
所谓共享,就是多个组件需要读取或者修改这个状态,如果是,那不用多想,应该放在 Store 上,因为 Store 上状态方便被多个组件共用,避免组件之间传递数据;如果不是,继续看第二步。
第二步,看这个组件被 unmount 之后重新被 mount,之前的状态是否需要保留。
举个简单例子,一个对话框组件。用户在对话框打开的时候输入了一些内容,不做提交直接关闭这个对话框,这时候对话框就被 unmount 了,然后重新打开这个对话框(也就是重新 mount),需求是否要求刚才输入的内容依然显示?如果是,那么应该把状态放在 Store 上,因为 React 组件在 unmount 之后其中的状态也随之消失了,要想在重新 mount 时重获之前的状态,只能把状态放在组件之外,Store 当然是一个好的选择;如果需求不要求重新 mount 时保持 unmount 之前的状态,继续看第三步。
第三步,到这一步,基本上可以确定,这个状态可以放在 React 组件中了。
主要掌握点
- Redux 中的基本概念 action、reducer 和 store;
- 使用 react-redux 会应用哪些设计模式;
- 如何设计 Redux 的 Store。
三、Mobx数据管理工具
简单直接的数据操作,不需要做dispatch,不必如redux走一遍严格的范式,调用action就能修改数据。而且适合使用多个store,当然使用configure配置可以禁止直接修改observable数据,只能使用action。
主要掌握点
- Mobx 的基本功能就是“观察者模式”
- decorator 是“装饰者模式”在 JavaScript 语言中的实现
- Mobx 通常利用 decorator 来使用
- 如何利用 mobx-react 来管理 React组件的状态
四、不同方式的对比
Redux 认为,数据的一致性很重要,为了保持数据的一致性,要求Store 中的数据尽量范式化,也就是减少一切不必要的冗余,为了限制对数据的修改,要求 Store 中数据是不可改的(Immutable),只能通过 action 触发 reducer 来更新 Store。
Mobx 也认为数据的一致性很重要,但是它认为解决问题的根本方法不是让数据范式化,而是不要给机会让数据变得不一致。所以,Mobx 鼓励数据干脆就“反范式化”,有冗余没问题,只要所有数据之间保持联动,改了一处,对应依赖这处的数据自动更新,那就不会发生数据不一致的问题。
主要区别:
- Redux 鼓励一个应用只用一个 Store,Mobx 鼓励使用多个 Store;
- Redux 使用“拉”的方式使用数据,这一点和 React是一致的,但 Mobx 使用“推”的方式使用数据,和 RxJS 这样的工具走得更近;
- Redux 鼓励数据范式化,减少冗余,Mobx 容许数据冗余,但同样能保持数据一致。
适用场景:
Mobx 应用小而简单 快速开发
Redux 大而复杂 需要长期维护
当然随着react的不断更新发展,也有可能出现react内部的数据共享机制。