React 中属性 props 和状态 state 说明及区别

react 中通过 props 和 state 实现组件间的通信,对数据进行传递、操作。

1. 属性 props

在组件中可以通过 props 传递数据。

正常情况下,props是外部传入的,组件内部也可以通过一些方式来设置属性的默认值,属性不能被组件自己更改,但是你可以通过父组件主动重新渲染的方式来传入新的 props。

通俗来讲,就是在使用一个组件的时候,可以把参数放在标签的属性当中,所有的属性都会作为组件 props 对象的键值。通过箭头函数创建的组件,需要通过函数的参数来接收props

来看一个使用 props 传参的例子:

import React from 'react';
import ReactDOM from 'react-dom';

//函数组件
function Box(props) {
    return <h1>Hello {props.name}!</h1>;
}
 
const element = <Box name="React"/>;
 
ReactDOM.render(
    element,
    document.getElementById('root')
);

1.1 设置默认值 defaultProps

在类组件中,可以通过组件类的 defaultProps 属性为 props 设置默认值。在父组件没有指定其值时,会使用这个默认值。propTypes 类型检查发生在 defaultProps 赋值后,所以类型检查也适用于 defaultProps。

import React from 'react';
import ReactDOM from 'react-dom';

class Box extends React.Component {
	//方式一:在类的内部设置静态属性
	static defaultProps={
		name:'react'
	}
	render() {
		return (
			<h1>Hello, {this.props.name},age:{this.props.age}</h1>
		);
	}
}

//方式二:在类的外面 设置defaultProps属性
Box.defaultProps = {
	age: 18
};
 
ReactDOM.render(
  <Box/>,
  document.getElementById('root')
);

1.2 props.children

每个组件都可以获取到 props.children。它包含组件的开始标签和结束标签之间的内容。类似vue中的插槽。

看下面这个例子:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Content extends Component {
  render() {
    return (
      <div>{this.props.children}</div>
    );
  }
}

class Title extends Component {
  render() {
    return (
      <div>欢迎进入{this.props.children} </div>
    );
  }
}

ReactDOM.render(
	<Content>
		<Title>React</Title>
		<p>这里是内容</p>
	</Content>,
	document.getElementById('root')
);

上面的代码中,在页面中渲染Content 的 props.children,最终会渲染出<Content></Content>标签中的所有内容,渲染页面的结果如下:
React porps

1.3 proptypes 类型检查

Props 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。

React.PropTypes 在 React v15.5 版本后已经移到了prop-types 库。

在终端中执行下面的命令,安装 prop-types 插件
yarn add prop-types -S

使用:

  1. 使用 import 导入 prop-types。
  2. prop-types 只能在类组件中做验证,不能在函数组件中做验证。
  3. 通过 类名.propTypes={},来定义属性规则,固定写法,注意大小写
  4. 类名.propTypes={} 中,键名为属性名,值为对属性的验证规则,例如Proptypes.string等。
  5. 可以指定一个自定义验证器。它在验证失败时应返回一个 Error 对象。自定义验证时,对象值为一个函数,函数的参数可以通过 arguments 打印。
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import Proptypes from 'prop-types';

class Box extends Component {
  render() {
    return (
      <div>
        姓名: {this.props.name},年龄:{this.props.age}
      </div>
    );
  }
}

Box.propTypes={
	name:Proptypes.string.isRequired,	//表示name为字符串类型,isRequired 必须要传
	age:function(props,propName){	//自定义验证
		if(props[propName]<20){
            return new Error(
                'Invalid prop `' + propName + '` Because it Less than 20.'
              )
        }
	}
}

ReactDOM.render(
  <Box name="xiaoming" age={18} />,
  document.getElementById('root')
);

prop-types 的验证规则:

规则 说明
.array 输入的类型为数组
.bool 输入的类型为布尔
.func 输入的类型为函数
.number 输入的类型为数值
.object 输入的类型为对象
.string 输入的类型为字符串
.symbol 输入的类型为symbol类型

以上为 js 的原生类型

规则 说明
.node 表示任何可被渲染的元素(包括数字、字符串、元素或数组) (或 Fragment) 也包含这些类型。
.element 表示一个 React 元素,确保传递给组件的 children 中只包含一个元素。
.elementType 表示一个 React 元素类型,即上面案例中的 Box
.instanceOf() 声明 prop 为是否为类的实例,这里使用 JS 的 instanceof 操作符
.oneOf() 指定 prop 只能是特定的值,指定它为枚举类型
.oneOfType() 一个对象可以是几种类型中的任意一个类型
.arrayOf() 指定一个数组由某一类型的元素组成,例如只能由数字组成的数组 .arrayOf(PropTypes.number)
.objectOf() 指定一个对象由某一类型的值组成,使用方法同 .arrayOf()
.shape() 指定一个对象由特定的类型值组成
.isRequired 在任何 PropTypes 属性后面加上 isRequired ,确保这个 prop 没有被提供时,会打印警告信息

部分语法使用如下:

import PropTypes from 'prop-types';

Box.propTypes = {
  // 你可以让你的 prop 只能是特定的值,指定它为
  // 枚举类型。
  optionalEnum: PropTypes.oneOf(['News', 'Photos']),

  // 一个对象可以是几种类型中的任意一个类型
  optionalUnion: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

  // 可以指定一个数组由某一类型的元素组成
  optionalArrayOf: PropTypes.arrayOf(PropTypes.number),

  // 可以指定一个对象由某一类型的值组成
  optionalObjectOf: PropTypes.objectOf(PropTypes.number),

  // 可以指定一个对象由特定的类型值组成
  optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }),
  
  // An object with warnings on extra properties
  optionalObjectWithStrictShape: PropTypes.exact({
    name: PropTypes.string,
    quantity: PropTypes.number
  }),   

2. 状态 state

state 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。

可以在类的构造函数中初始化状态 state,也可以在类中直接定义属性(下面2.1中的写法)来初始化状态 state 。通过 this.state 获取 state 中的数据内容。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
  constructor() {
    super();
    this.state = {
      name: 'xiaoming',
      age: 18
    }
  }
  render() {
    return (
      <div>
        姓名: {this.state.name},年龄:{this.state.age}
      </div>
    );
  }
}

ReactDOM.render(
  <Box />,
  document.getElementById('root')
);

2.1 setState()

修改 state 中的数据内容,有以下三种方式:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

class Box extends Component {
    state={
        name:"xiaoming"
    }
    componentDidMount(){
    	//第一种修改方式
    	this.setState({name:'zhangsan'});

		//第二种修改方式
        this.state.name="sssss";
        this.setState({});

		//第三种修改方式
		this.setState((preState,props)=>{
		    return{
		        name:preState.name+'ccc'
		    }
		},()=>{ 
		    //数据修改完成后的回调函数
		})
    }
    render() {
      return (
        <div>姓名: {this.state.name}</div>
      );
    }
  }
  
ReactDOM.render(
  <Box />,
  document.getElementById('root')
);

当使用第二种方式的时候,虽然修改后的数据也在浏览器端渲染出来了,但是在控制台中会输出下面的警告信息,所以不建议直接使用 this.state 修改数据

在这里插入图片描述
react 和 vue 中对 state 中数据的修改都是异步的,在vue中可以使用$nextTick() 方法,获取修改后的数据内容,在 react 中通过使用上面的第三种方法,获取修改后的数据内容。

setState 对数据的更新,会做 merge 合并的操作,即不会覆盖原来的数据内容,会把你提供的对象合并到当前的 state中。

2.2 状态提升

如果多个组件要实现数据共享,可以将数据提升到父组件中,对数据的操作统一在父组件中进行。

3. 属性和状态的区别

props 和 state 的相似点:

  • 都是 js 对象,更新数据后会都会触发 render() 更新;
  • 都可以设置默认值;props 通过 defaultProps 属性设置默认值;state 直接在定义的时候设置初始值;

props 和 state 的不同点:

  • 属性是从父组件获取的,状态是在当前组件中定义的;
  • 属性值只能由父组件修改,状态值只能由当前组件修改;
  • 属性主要用于父组件和子组件间的通信,在组件内部是无法修改参数的;
  • 状态是给自己用的,在内部初始化,被自己修改,在外部是不能修改的,内部通过 setState 修改,会触发render函数;

总结:state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据

发布了138 篇原创文章 · 获赞 51 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Charissa2017/article/details/105597975