【React】深入理解prop、state与render函数

本文详细探讨了React中的prop与state的区别,prop用于定义外部接口,不应被组件修改,而state用于组件内部状态管理。文章介绍了prop的设置方式、数据类型,强调了修改prop的不当之处。同时讲解了state的管理,包括何时使用constructor初始化state,以及this.setState的重要性。最后,阐述了render函数作为纯函数的角色,它是根据props和state来决定组件的渲染内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

prop 与 state 的对比

  • prop 用于定义外部接口,state用于记录内部状态;
  • prop的赋值在外部世界使用组件时,state的赋值在组件内部;
  • 组件不应该改变 prop 的值,而 state 存在的目的就是让组件来改变的;

关于 prop

设置 props 的方式

1)可以在组件挂载时设置props:
<script type="text/babel">
    var HelloWorld = React.createClass({
        render: function(){
            return <p>Hello, {this.props.name ? this.props.name : "World"}</p>
        }
    });
    React.render(<HelloWorld name="Sam" />, document.body);
</script>
2)也可以通过调用组件实例的setProps()方法来设置props(在ES6中将被禁用,这个方法不支持ES6类组件React.Component扩展。)
<script type="text/babel">
    var HelloWorld = React.createClass({
        render: function(){
            return <p>Hello, {this.props.name ? this.props.name : "World"}</p>
        }
    });
    var instance = React.render(<HelloWorld/>, document.body);
    instance.setProps({name: 'Tim'});
</script>

prop 支持的数据类型

我们先从外部世界来看,prop是如何使用的,在下面的JSX代码片段中,就使用了prop:

<SampleButton id="sample" borderWidth={2} onClick={onButtonClick} style={{color: "red"}} />

  在上面的例子中,创建了名为 SampleButton 的组件实例,使用了名字分别为id、borderWidth、onClick和style的 prop,看起来,React组件的 prop 很像是HTML元素的属性,不过,HTML组件属性的值都是字符串类型,即使是内嵌的JavaScript,也依然是字符串形式表示代码。React组件的 prop 所能支持的类型则丰富得多,可以是任何一种JavaScript语言支持的数据类型。

  比如在上面的SampleButton中,borderWidth就是数字类型,onClick是函数类型,style的值是一个包含color字段的对象,当 prop 的类型不是字符串类型时,在JSX中必须用花括号{}把 prop 值包住,所以style的值有两层花括号,外层花括号代表是JSX的语法,内层的花括号代表这是一个对象常量。

  当外部世界要传递一些数据给React组件,一个最直接的方式就是通过 prop;同样,React组件要反馈数据给外部世界,也可以用 prop,因为 prop 的类型不限于纯数据,也可以是函数,函数类型的 prop 等于让父组件交给了子组件一个回调函数,子组件在恰当的实际调用函数类型的 prop,可以带上必要的参数,这样就可以反过来把信息传递给外部世界。

修改 props 是不好的

如果你不知道要设置哪些 Props,那么现在最好不要设置它:

 var component = <Component />;
  component.props.foo = x; // 不好
  component.props.bar = y; // 同样不好

  这样是反模式,因为 React 不能帮你检查属性类型(propTypes)。这样即使你的属性类型有错误也不能得到清晰的错误提示。

  Props 应该被当作禁止修改的。修改 props 对象可能会导致预料之外的结果,所以最好不要去修改 props 对象。
  

展开属性(Spread Attributes)

现在你可以使用 JSX 的新特性 - 展开属性:

var props = {};
  props.foo = x;
  props.bar = y;
  var component = <Component {...props} />;

传入对象的属性会被复制到组件内。

React中的map遍历

var names = ['Alice', 'Emily', 'Kate'];

React.render(
    <div>
    {
        names.map(function (name) {
            return <div>Hello, {name}!</div>
        })
    }
    </div>,
    document.getElementById('example')
);

this.props.children 遍历所有子节点

  this.props 对象的属性与组件的属性一一对应,但是有一个例外,就是 this.props.children 属性。它表示组件的所有子节点

var NotesList = React.createClass({
    render: function() {
        return (
        <ol>
        {
            React.Children.map(this.props.children, function (child) {
                return <li>{child}</li>;
            })
        }
        </ol>
        );
    }
});
ReactDOM.render(
    <NotesList>
        <span>hello</span>
        <span>world</span>
    </NotesList>,
    document.body
);

  这里需要注意, this.props.children 的值有三种可能:如果当前组件没有子节点,它就是 undefined ; 如果有一个子节点,数据类型是 object ;如果有多个子节点,数据类型就是 array 。所以,处理 this.props.children 的时候要小心。

  React 提供一个工具方法 React.Children 来处理 this.props.children 。我们可以用 React.Children.map 来遍历子节点,而不用担心 this.props.children 的数据类型是 undefined 还是 object。

关于super(props)

class Counter extends Component{
    constructor(props){
        super(props);

        this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
        this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

        this.state = {
            count: props.initValue || 0
        }
    }
}

  如果一个组件需要定义自己的构造函数,一定要记得在构造函数的第一行通过 super 调用父类也就是React.Component的构造函数。如果在构造函数中没有调用super(props),那么组件实例被构造之后,类实例的所有成员函数就无法通过 this.props 访问到父组件传递过来的 props 值。

在Counter的构造函数中还给两个成员函数绑定了当前this的执行环境,因为ES6方法创造的React组件类并不自动给我们绑定this到当前实例对象。

关于 state

this.state 与 this.setState

  在代码中,通过 this.state 可以读取到组件的当前 state。值得注意的是,我们改变组件 state 必须要使用 this.setState 函数,而不能直接去修改 this.state

  直接修改 this.state 的值,虽然事实上改变了组件的内部状态,但只是野蛮地修改了 state,却没有驱动组件进行重新渲染,既然组件没有重新渲染,当然不会反应 this.state 值的变化;而 this.setState() 函数所做的事情,首先是改变 this.state 的值,然后驱动组件经历更新过程,这样才有机会让 this.state 里新的值出现在界面上。

什么时候需要组件的构造函数 constructor

  要注意,并不是每个组件都需要定义自己的构造函数。无状态的React组件往往就不需要定义构造函数,一个React组件需要构造函数,往往是为了下面的目的:

  • 初始化 state,因为组件生命周期中任何函数都可能要访问 state,那么整个生命周期中第一个被调用的构造函数自然是初始化 state 最理想的地方;
  • 绑定成员函数的 this 环境;
      在ES6语法下,类的每个成员函数在执行时的 this 并不是和类实例自动绑定的。而在构造函数中,this 就是当前组件实例,所以,为了方便将来的调用,往往在构造函数中将这个实例的特定函数绑定 this 为当前实例。
    以 Counter 组件为例,我们的构造函数有这样如下的代码:
this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

  这两条语句的作用,就是通过 bind 方法让当前实例中的 onClickIncrementButton 和 onClickDecrementButton 函数被调用时,this 始终是指向当前组件实例。

初始化 state 的区别(ES5与ES6)

const Sample = React.createClass({
    getInitialState: function(){        // 初始化组件的 this.state
        return {foo: 'bar'};
    },
    getDefaultProps: function(){        // 设置组件 props 的初始值
        return {sampleProp: 0}
    }
});
class Sample extends React.Component {
    constructor(props){                 // 初始化组件的 this.state
        super(props);
        this.state = {foo: 'bar'};
    }
};

Sample.defaultProps = {sampleProp: 0};  // 设置组件 props 的初始值

  React.createClass 已经被Facebook官方逐渐废弃,但是在互联网上还能搜索到很多使用 React.createClass 的教材,虽然强烈建议不再要使用 React.createClass ,但是如果读者你真的要用的话,需要注意关于 getInitialState 只出现在装载过程中,也就是说在一个组件的整个生命周期过程中,这个函数只被调用一次,不要在里面放置预期会被多次执行的代码。

render 函数

  通常一个组件要发挥作用,总是要渲染一些东西,render 函数并不做实际的渲染动作,它只是返回一个JSX描述的结构,最终由React来操作渲染过程。(render函数并不往DOM树上渲染或者装载内容,它只是返回一个JSX表示的对象,然后由React库来根据返回对象决定如何渲染。而React库肯定是要把所有组件返回的结果综合起来,才能知道该如何产生对应的DOM修改。)

  当然,某些特殊组件的作用不是渲染界面,或者,组件在某些情况下选择没有东西可画,那就让render函数返回一个null或者false,等于告诉React,这个组件这次不需要渲染任何DOM元素。

  需要注意,render 函数应该是一个纯函数,完全根据 this.state 和this.props 来决定返回的结果,而且不要产生任何副作用。在 render 函数中去调用 this.setState 毫无疑问是错误的,因为一个纯函数不应该引起状态的改变。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值