React属性与状态
React属性与状态
属性和状态作为组件之间数据流动的途径。
单向数据流
单向数据流:更新 DOM 的数据总是从顶层流下来,用户事件不直接操作 DOM,而是操作顶层数据。这些数据从顶层流下来同时更新了 DOM。这就如同「云腾致雨,露结成霜」一样是大自然的规律。
在React中,数据的流向是单向的——从父节点传递到子节点,因而组件是简单且易于把握的,它们只需要从父节点获取props渲染即可。如果顶层组件的某个prop改变了,React会递归地向下遍历整颗组件树,重新渲染所有使用这个属性的组件。
属性(props)
属性(props):类似 HTML 中的属性,绘制时可在标签中添加属性,然后在组件中通过 this.props.属性名 获取。props属性是父组件控制子组件的单向数据流传输的关键。
使用props注意事项
- props类似HTML的属性
- props只读,不能使用this.props修改
- props用于整个组件树种传递数据和配置
- 访问props,需要通过this.props.属性名 获取传递的属性值
属性使用键值对
// 这个键值对可以是字符串: <Hello name="winty"/> // 可以是大括号:大括号包裹的求值表达式 <Hello name={123}/> <Hello name={"king"}/> // 可以是一个数组:这样属性拿到的也是一个数组 <Hello name={[1,2,3]}/> 传入数组 // 可传入变量:变量可以在外部定义,在组件内去引用 变量的值可为任何类型 <Hello name={winty}/> 变量
在组件中使用属性
class HelloMessage extends React.Component{ render() { return <h1>Hello {this.props.name}</h1>; } } ReactDOM.render(<HelloMessage name="John" />, document.getElementById('example') );
其中props 可以认为是一个配置接口,在使用 HelloMessage 组件的时候,传入的任何html属性都会保存在组件 this.props 中。
属性使用展开定义
var attr={ one:"11", two:"22" }
这样定义的话,理论上使用应该是one={attr.one}这样调用,但是这样写起来比较繁琐,而且如果数据被修改,就需要对应修改相应的赋值,并且无法动态地设置属性,所以react中添加了一种展开语法:
<Hello {...attr}/> //也就是三个点加上对象名称
这样使用展开语法,react就会自动把对象中的变量和值当作是属性的赋值,所以Hello实际上就拿到了one、two两个属性,如果没有三个点的话,Hello拿到的实际上就是attr对象,使用的时候还需要自己从中取出变量和值
为组件定义默认属性
可以通过如下两种方式来设置属性的默认值。
//方式1: class Test extends React.Component{ static defaultProps = { name:'everyone' } constructor(props){ super(props); } render(){ return (<div>{this.props.name}</div>); } } //方式2: class Test extends React.Component{ constructor(props){ super(props); } render(){ return (<div>{this.props.name}</div>); } } Test.defaultProps = { name:'everyone' }
使用 PropTypes 进行类型检查
注意: 从 React v15.5 开始 ,React.PropTypes 助手函数已被弃用,我们建议使用 prop-types 库 来定义contextTypes。
PropTypes包
名称 | 链接 |
---|---|
PropTypes | https://www.npmjs.com/package/prop-types |
安装
npm install prop-types
引入
import PropTypes from 'prop-types'; // ES6 var PropTypes = require('prop-types'); // ES5 with npm
使用
PropsTypes是React中用来定义props的类型,不符合定义好的类型会报错。建议可复用组件要使用prop验证!接着上面的列子设置PropsTypes如下:
# 写法一: class Test extends React.Component{ static defaultProps = { name:'everyone' } constructor(props){ super(props); } render(){ return (<div>{this.props.name}</div>); } } Test.propTypes = { name:PropTypes.string } <Test name="'houningzhou'" /> # 写法二: class Test extends React.Component{ static defaultProps = { name:'everyone' } static propTypes = { name:PropTypes.string } constructor(props){ super(props); } render(){ return (<div>{this.props.name}</div>); } } <Test name="'houningzhou'" /> React.PropTypes 提供很多验证器 (validator) 来验证传入数据的有效性。官方定义的验证器如下,不是使用ES6语法。 MyComponent.propTypes = { // 可以声明 prop 为指定的 JS 基本类型。默认情况下,这些 prop 都是可传可不传的。 optionalArray: PropTypes.array, optionalBool: PropTypes.bool, optionalFunc: PropTypes.func, optionalNumber: PropTypes.number, optionalObject: PropTypes.object, optionalString: PropTypes.string, optionalSymbol: PropTypes.symbol, // 所有可以被渲染的对象:数字,字符串,DOM 元素或包含这些类型的数组(or fragment) 。 optionalNode: PropTypes.node, // React 元素 optionalElement: PropTypes.element, // 你同样可以断言一个 prop 是一个类的实例。 用 JS 的 instanceof 操作符声明 prop 为类的实例。 optionalMessage: PropTypes.instanceOf(Message), // 你可以用 enum 的方式 确保你的 prop 被限定为指定值。 optionalEnum: PropTypes.oneOf(['News', 'Photos']), // 指定的多个对象类型中的一个 optionalUnion: PropTypes.oneOfType([ PropTypes.string, PropTypes.number, PropTypes.instanceOf(Message) ]), // 指定类型组成的数组 optionalArrayOf: PropTypes.arrayOf(React.PropTypes.number), // 指定类型的属性构成的对象 optionalObjectOf: PropTypes.objectOf(React.PropTypes.number), // 特定形状参数的对象 optionalObjectWithShape: PropTypes.shape({ color: PropTypes.string, fontSize: PropTypes.number }), // 你可以在任意东西后面加上 `isRequired` 来确保 如果 prop 没有提供 就会显示一个警告。 requiredFunc: PropTypes.func.isRequired, // 不可空的任意类型 requiredAny: PropTypes.any.isRequired, // 你可以自定义一个验证器。如果验证失败需要返回一个 Error 对象。 // 不要直接使用 `console.warn` 或抛异常, // 因为这在 `oneOfType` 里不起作用。 customProp: function(props, propName, componentName) { if (!/matchme/.test(props[propName])) { return new Error('Validation failed!'); } } };
状态(State)
State介绍
状态 state:使用this.state来引用,state本身就是状态的意思,状态指的是事物所处的状况,状况就是环境。
通常使用state存储简单的视图状态,比如说下拉框是否显示、单选 是否选中,或者需要自身去维护的变化数据等。
注意:
状态是事物自己处理的,不和属性一样,属性是天生的,事物一般来说没有办法修改。
状态是事物本身的是事物的私有属性,也就是由自己决定,父组件和子组件都不能改变他。给定了状态就一定知道结果是什么。
组件中用到的某个变量是不是应该作为组件State,可以通过下面的5条依据进行判断:
- 这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个状态。
- 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
- 这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个状态。
- 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性,例如组件中用到的定时器,就应该直接定义为
this.timer
,而不是this.state.timer
。 - 这个变量修改后,是否要更新组件?是,那么它是一个状态。
设置默认state
class Test extends React.Component{ constructor(props){ super(props); //默认状态 this.state = { isShow:true } } render(){ return <div style={display:this.state.isShow?'block':'none'}>This is component!</div> } }
修改state
直接修改state,组件并不会重新重发render。例如:
// 错误
this.state.title = 'React';
state可以通过setState来修改,只要setState被调用,render就会被调用。如果render函数的返回值有变化,虚拟DOM就会更新,真实的DOM也会被更新,最终用户可以在浏览器中看到变化。
this.setState({
})
setState会触发diff算法:判断state和页面结果的区别,是否需要更新
案例
class Timer extends React.Component{ constructor(props){ super(props); this.state = { sec: 1 } } add(){ this.setState({sec:this.state.sec+1}); }, componentDidMount(){ setInterval(this.add,1000); }, render(){ return ( <div style={this.style}>过去了:{this.state.sec}秒钟</div>); } } ReactDOM.render(<Timer/>,document.getElementById('demo'));
State 的更新是异步的
当我们调用setState时,组件的state病不会立即修改,setState只是把要修改的状态放入到一个队列中,React会优化真正的执行时机,并且出于性能原因,可能会将多次setState的状态修改合并在一次状态修改。所以不要依赖当前的state去计算下一个state。
state 和 props 之间有什么区别?
props 和 state 都是在改变时会触发一次重新渲染的 JavaScript 对象。虽然两者都具有影响渲染输出的信息,但它们在一个重要方面是不同的: props 传递到组件(类似于函数参数),而 state 是在组件内管理的(类似于函数中声明的变量)。
state
不要在state中保存计算出的值,而应该保存简单的数据。
状态:下拉菜单是否显示、输入框的值、变化的数据、选项卡的选中状态
props
props作为数据源存在,使用props在整个组件树中传递数据。