react 组件state
state所代表的一个组件UI呈现的完整状态集又可以分成两类数据:用作渲染组件时使用到的数据的来源以及用作组件UI展现形式的判断依据。state还容易和props以及组件的普通属性混淆。我们的组件都是使用ES6的class定义的,所以组件的属性其实也就是class的属性。在ES6中,可以使用this.{属性名}定义一个class的属性,也可以说属性是直接挂载到this下的变量。因此,state、props实际上也是组件的属性,只不过它们是React为我们在Component class中预定义好的属性。除了state、props以外的其他组件属性称为组件的普通属性。
state和props都直接和组件的UI渲染有关,它们的变化都会触发组件重新渲染,但props对于使用它的组件来说是只读的,是通过父组件传递过来的,要想修改props,只能在父组件中修改;而state是组件内部自己维护的状态,是可变的。这个变量是否通过props从父组件中获取,如果是,那么它不是一个状态。如果变量在组件的生命周期中保持不变,那么它不是一个状态。变量如果通过其他状态或属性可以计算得到,那它不是一个状态。这个变量是否在组件的render方法中使用。如果不是,那么它不是一个状态。这情况下变量更适合定义为组件的一个普通属性。
调用setState时,组件的state并不会立即改变,setState只是把要修改的状态放入一个队列中,React会优化真正的执行时机,并且出于性能原因,可能会将多次setState的状态修改合并成一次状态修改。所以不能依赖当前的state,计算下一个state。当真正执行状态修改时,依赖的this.state并不能保证是最新的state,因为React会把多次state的修改合并成一次,这时this.state还是这几次state修改前的state。另外,需要注意的是,同样不能依赖当前的props计算下一个状态,因为props的更新也是异步的。
如果想同步更新,有这样的需求,可以使用另一个接收一个函数作为参数的setState,这个函数有两个参数,第一个是当前最新状态的前一个状态preState,第二个参数是当前最新的属性props。
this.setState((preState,props)=>({ counter:preState.quantity+1; }))
React官方建议把state当做不可变对象,一方面,直接修改this.state,组件并不会重新render;另一方面,state中包含的所有状态都应该是不可变对象。当state中某个状态发生变化时,应该重新创建这个状态对象,而不是直接修改原来的状态。状态的类型可以分成三种情况:状态的类型是不可变类型(数字、字符串、布尔值、null、undefined)。因为状态是不可变类型,所以直接给要修改的状态赋一个新值即可。状态的类型是数组。向数组中增加数据时,可使用素组的concat方法或es6的数组扩展语法:方法一:使用preState、concat创建新数组。方法二使用ES6解构语法。[..preState.data,'data'];当从数组中截取部分元素作为新状态时,可使用数组的slice方法,当从books中过滤部分元素后,作为新状态时,可使用数组的filter方法:preState.data.filter(item=>{return item !='React'}。
注意,不要使用push、pop、shift、unshift、splice等方法修改数组类型的状态,因为这些方法都是在原数组的基础上修改的,而concat、slice、filter会返回一个新的数组。
状态的类型是普通对象(不包含字符串、数组)使用es6语法。为什么React推荐组件的状态是不可变对象呢?一方面是因为对不可变对象的修改会返回一个新对象,不需要担心原有对象在不小心的情况下被修改导致的错误,方便程序的管理和调试;另一方面是出于性能考虑,当对象组件状态都是不可变对象时,在组件的shouldComponentUpdate方法中仅需要比较前后两次状态对象的引用就可以判断状态是否真的改变,从而避免不必要的render调用。
componentWillMount会在组件被挂载前调用,因此从时间上来讲,在componentWillMount中执行服务器通信要早于在componentDidMount中执行,执行得越早意味着服务器数据越能更快地返回组件。但实际上,componentWillMount与componentDidMount执行得时间差微乎其微,完全可以忽略不计。componentDidMount是执行组件与服务器通信的最佳方法,原因主要有两个:1在componentDidMount中执行服务器通信可以保证获取到数据时,组件已经处于挂载状态,这时即使要直接操作DOM也是安全的,而componentWillMount无法保证这一点。2当组件在服务器端渲染时,componentWillMount会被调用两次,一次是在服务器端,另一次是在浏览器端,而componentDidMount能保证在任何情况下只会被调用一次,从而不会发送多余的数据请求。
当组件所处层级太深时,往往需要经过很多层的props传递才能将所需的数据或回调函数传递给使用组件。这时,以props作为桥梁的组件通信方式便会显得很繁琐。React提供了一个context上下文,让任意层级的子组件都可以获取父组件的状态和方法。创建context的方式是:在提供context的组件内新增一个getChildContext方法,返回context对象,然后在组件的childContextTypes属性上定义context对象的属性的类型信息。context将来可能被修改或被废弃。所以用到的时候再说吧。
Redux专门的状态管理库来实现组件通信和组件状态的管理。