state与状态机思维
状态机的四大概念。
State ,状态。一个状态机至少要包含两个状态。例如上面灯泡的例子,有 灯泡亮和 灯泡灭两个状态。
Event ,事件。事件就是执行某个操作的触发条件或者口令。对于灯泡,“打开开关”就是一个事件。
Action ,动作。事件发生以后要执行动作。例如事件是“打开开关”,动作是“开灯”。编程的时候,一个 Action 一般就对应一个函数。
Transition ,变换。也就是从一个状态变化为另一个状态。例如“开灯过程”就是一个变换。
React将每一种UI的状态都看做为一个简单的状态机,那么任意一个UI场景就是状态机中的一种状态。
根据决定状态的状态机变量的值,React框架渲染出状态机的当前状态——对于开发者来说,单个UI场景就被渲染出来了。随着状态机变量值的变化,UI状态机也在不停的改变状态,UI场景也随着不断渲染。这样一个过程可以很轻松的做到数据与UI保持一致。
在RN的开发中,开发者需要将状态机变量视为“不可变的常量”,在开发者的代码中,永远不要出现 this.state.变量名 = value 这样的语句。这样的语句对于JS来讲是合法的并且可以被正确执行,但是从RN开发原则的角度说,它是不合法的。当开发者需要改变状态机变量的值时,一定要并且只能使用this.setState函数。
对于RN开发者来说,RN开发的一个重要原则就是:
“努力让自定义的RN组件成为无状态的RN组件”。
或者说在不影响程序结构的情况下,尽可能减少有状态的React-native组件的数量。
那么,哪些RN组件必须有状态机变量呢?
当一个组件需要处理用户的输入,或者需要处理网络侧发给应用程序的数据,或者需要处理超时事件,或者需要处理自己订阅的事件消息等不可预知的输入型事件时。它就必须要有对应的状态机变量。
努力让用户的自定义RN组件成为无状态的RN组件,减少状态机变量的数量,可以让代码的框架更清晰。
一个好的设计思路是:创建多个只负责渲染数据的无状态React-Native组件,将它们封装在一个有状态的RN组件中,并把这个有状态的RN组件的状态机变量的值通过props传给无状态的RN组件。这就是React-Redux的设计原理。
状态机变量的合并
根据上面的状态机思维,当我们需要重新渲染UI时,只要合并状态机变量即可,常见的合并状态机变量的API有setState和forceUpdate。
forceUpdate
如果开发者因为某种原因,使得UI中可变数据的来源必须从状态机变量和属性外获得,那么就需要这个变量来进行界面的强制刷新。
它会导致不经优化的全部刷新,即调用forceUpdate函数导致的重新渲染过程,将不会调用shouldComponentUpdate来检查并重新渲染。因此会导致大量无意义的刷新,只要需要UI进行立即刷新的特殊情况才需要使用。(它是同步的函数,在UI线程上执行刷新)
setState
与之不同的是 setState(nextState, callback) 函数在调用后并不会进行同步的刷新,而是异步的刷新,它需要经过优化的步骤,重新计算哪里发生了改变,哪里需要重新渲染。所以,需要指出的是如果setState中的需要合并的新的状态机变量与原本的没有任何的变化,它将不会进行渲染。
同时,因为setState是异步的,所以如果我们需要setState渲染后的某些属性来进行相应的操作,那么就需要使用回调函数。例如下面的例子:
如果我们界面中进行了控件的产生和销毁,那么象征该控件的ref只有在被渲染到屏幕上后该组件的ref才存在,但是这就存在一个问题即setState是异步的,我们执行以下的操作就会报ref is undefined的错误:
this.setState({xxx:value});
// 特别的,当xxx与value变量名重名时,可以省略,即setState({value})与setState({value:value})等价
this.refs.xxx
这是因为setState是异步的操作,当它被调用时主线程继续向下执行,但是这时还没有渲染完成,即ref为undefined的状态,这时就要采用setState的回调函数来完成,它等价于componentDidUpdate,与componentUpdate相比较好的一点是,它可以在多个setState中根据更新的数据的不同定制不同的回调函数,避免componentDidUpdate函数体过长。
`
最后不会出现问题的调用方式就是:
this.setState({[key]:value},() => {
this.ref[xxx]
});
shouldComponentUpdate
RN框架使用shouldComponentUpdate函数来判断接下来是否进行渲染,这个函数的原型是 boolean shouldComponentUpdate(nextProps,nextState)。这个函数会传入两个对象,分别代表接下来渲染所基于的props与state。
与数据库中的触发器很类似,在执行shouldComponentUpdate这个函数时,组件原本的props和state并没有发生改变,你还可以用this.state和this.props访问它。所以可以进行当前状态和更新状态的比较来判断是否需要进行重新渲染。
如果RN组件实现了这个函数,那么在执行重新渲染时,会先调用这个函数。可以在这个函数中进行一定的判断,如果这个函数返回false,那么RN将放弃渲染该组件。
借鉴链接:https://blog.csdn.net/kkk96291/article/details/110659859
网友评论