React入门 Part6

设计合适的State

组件state必须能够代表一个组件UI呈现的完整状态集,即组件的任何UI改变都可以从state的变化中反映出来;

同时,state还必须代表一个组件UI呈现的最小状态集,即state中的所有状态都用于反映组件UI的变化,没有任何多余的状态,也不应该存在通过其他状态计算而来的中间状态。

state所代表的一个组件UI呈现的完整状态集又可以分为两类数据:

  • 用作渲染组件时用到的数据的来源;
  • 用于组件UI展现形式的判断依据;

示例代码:

import React from 'react';

class Hello extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            user: 'React',
            display: true
        }
    }

    render() {
        return(
            <div>
                {
                    this.state.display ? <h1>你好,{this.state.user}</h1> : null
                }
            </div>
        );
    }
}

export default Hello;

state还容易和props以及组件的普通属性混淆。

在ES 6中,可以使用this.{属性名}定义一个class的属性,也可以说属性是直接挂载到this下的变量。

因此,state、props实际上也是组件的属性,只不过是React在Component中已经预设。

除了props和state以外的其他组件属性称之为组件的普通属性。

假设一个组件需要显示当前时间,并且这个时间每秒都会自动更新,这个组件内就需要定义一个计时器,在这个计时器中每隔1s更新一次组件的state。这个计时器变量并不适合定义在组件的state中,因为它并不代表组件UI呈现状态,它只是用来改变组件的state,这个时候就到了组件的普通属性发挥作用的时候。

示例代码:

import React from 'react';

class NewHello extends React.Component {
    constructor(props) {
        super(props);
        //定义普通属性
        this.timer = null;
        this.state = {
            date : new Date()
        }
        this.updateDate = this.updateDate.bind(this);
    }

    componentDidMount() {
        this.timer = setInterval(this.updateDate, 1000);
    }

    componentWillUnmount() {
        clearInterval(this.timer);
    }

    updateDate() {
        this.setState({
            date: new Date()
        });
    }

    render() {
        return(
            <div>
                <h1>当前时间:{this.state.date.toString()}</h1>
            </div>
        );
    }
}

export default NewHello;

当在组件中需要用到一个变量,并且它与组件的渲染无关时,就应该把这个变量定义为组件的普通属性,直接挂载到this下,而不是作为组件的state。还有个更加直观的判断方法:组件的render方法有没有用到这个变量,如果没有用到,那么这就是个普通属性。

props和state的区别是什么?state和props都直接和组件的UI渲染有关,它们的变化都会触发组件的重新渲染,但是props对于使用它的组件来说是只读的,是通过父组件传递过来的,要想修改props只能在父组件中修改,而state是组件内部自己维护的状态,是可变的。

组件中用到的一个变量是不是应该作为state,可以通过以下依据进行判断:

  • 此变量是否通过props从父组件获取
    • 如果是,那么此变量不是一个state;
  • 此变量是否在组件的整个生命周期都保持不变
    • 如果是,那么此变量不是一个state;
  • 此变量是否可以通过其他state或者props计算得到
    • 如果是,那么此变量不是一个state;
  • 此变量是否在组件的render方法中使用
    • 如果不是,那么此变量不是一个state;

正确修改state

state可以通过this.state.{属性}的方式直接获取,当修改state时需要注意:

  • 不能直接修改state

    直接修改state并不会触发组件重新渲染

    this.state.title = 'new value';
    

    需要用setState()方法才可以触发重新渲染

    this.setState({title: 'new value'});
    
  • state的更新是异步的

    调用setState时,组件的state并不会立即改变,setState只是会把要修改的状态放入一个队列中,React会优化真正的执行时机,并且处于性能原因,可能会将多次setState的状态修改合并成一次修改状态。

    所以不要依赖当前的state,计算下一个state。

    当真正执行状态修改时,依赖的this.state并不能保证是最新的state,因为React会把多次state的修改合并成一次,这时this.state还是这几次state修改前的state。

    也不能依赖当前props计算下一个state,因为props也是异步更新的。

  • state的更新是一个合并的过程

    当调用setState修改组件的状态时,只需要传入发生改变的state即可,而不是组件完整的state,因为组件state的更新过程是一个合并的过程。

state与不可变对象

React官方建议把state当作不可变对象,一方面,直接修改this.state,组件并不会重新render,另一方面,state中包含的所有状态都应该是不可变的对象。

当state中的某个状态发生改变时,应该重新创建这个状态对象,而不是直接修改原来的对象。

根据状态的类型可以分为以下三类:

  • 状态的类型是不可变类型

    不可变类型包括:数字、字符串、布尔值、null和undefined;

    因为是不可变类型,因此可以直接给要修改的状态赋新值。

  • 状态的类型是数组

    像数组中添加元素时:

    使用preState、concat创建新的数组:

    this.setState(preState => ({
        values: preState.values.concat([newValue])
    }));
    

    使用ES 6 spread syntax:

    this.setState(preState => ({
    	value: [...preState.values, newValue]
    }));
    

    截取数组中部分元素时,使用数组的slice方法:

    this.setState(preState => ({
    	value: preState.values.slice(startIndex, endIndex)
    }));
    

    过滤数组中部分元素时,可以使用数组的filter方法:

    this.setState(preState => ({
    	value: preState.values.filter(item => {
            return item !== aimValue
        })
    }));
    
  • 状态的类型是普通对象

    普通对象不包括字符串和数组。

    使用ES 6的Object.assgin方法:

    this.setState(preState => ({
    	value: Object.assgin(targetObj, preState.obj, sourceObj)
    }));
    

    使用对象扩展语法:

    this.setState(preState => ({
    	value: {...preState.obj, aimAttr: newValue}
    }));
    

为什么React推荐组件的状态是不可变对象呢?

一方面是因为对不可变对象的修改会返回一个新的对象,不需要担心原有对象在不小心的情况下被修改导致的错误,方便程序的管理和调试;

另一方面是因为处于性能考虑,当对象组件状态都是不可变对象时,在组件的shouldComponentUpdate方法中仅需要比较前后两次state对象的引用就可以判断state是否真的改变,从而避免不必要的render调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值