属性验证和默认值, refs, state
一.属性验证和默认值
React内置属性类型,主要依靠React.PropTypes
这个静态属性,这样在书写时,可以防止属性类型写错。对应的类型为:
- Arrays --->
React.PropTypes.array
- Boolean --->
React.PropTypes.bool
- Numbers --->
React.PropTypes.number
- Strings --->
React.PropTypes.string
- Objects --->
React.PropTypes.object
- Functions --->
React.PropTypes.func
我们知道创建组件有3种方式,3种方式添加属性验证和默认值略有差异
1.React.createClass()
1.React.createClass()创建组件
Ⅰ.通过对象中的
propTypes
属性来表示默认类型
import React, { createClass, PropTypes } from "react";
const Summary = createClass({
displayName: "Summary",
// 属性验证
propTypes: {
ingredients: PropTypes.array,
steps: PropTypes.array,
title: PropTypes.string
},
render() {
const {ingredients, steps, title} = this.props;
return (
{title}
// 调用数组的length属性
{ingredients.length} Ingredients
{steps.length} Steps
);
}
})
上面的属性可以知道 Summary
组件的3个属性ingredients, steps为数组, title为字符串,如果传入不正确的数据类型,将会抛出错误,比如:
import { render } from "react-dom";
render(
,
document.body
);
// ingredients 和 steps 写成 字符串类型 抛出错误
// Failed propTypes: Invalid prop 'ingredients' of type 'string'
// supplied to Constructor, expected `array`
如果不写某个属性呢?
render(
,
document.body
);
由于返回的react元素中调用了数组的'length'属性,如果不提供该属性应用会抛出错误
"Uncaught TypeError: Cannot read property 'length' of undefined"
Ⅱ. React 提供了一个
isRequired
属性,其功能和html标签中'required'一致
const Summary = createClass({
displayName: "Summary",
// 属性验证
propTypes: {
// 使用 isRequired 表明该属性为必需属性
ingredients: PropTypes.array.isRequired,
steps: PropTypes.array.isRequired,
title: PropTypes.string
},
render() {
const {ingredients, steps, title} = this.props;
return (
{title}
// 调用数组的length属性
{ingredients.length} Ingredients
{steps.length} Steps
);
}
})
这个组件实际需要的是ingredients和steps的长度,为一个数值类型,可以将上面的改写为:
const Summary = createClass({
displayName: "Summary",
// 属性验证
propTypes: {
ingredients: PropTypes.number.isRequired,
steps: PropTypes.isRequired,
title: PropTypes.string
},
render() {
const {ingredients, steps, title} = this.props;
return (
{title}
// 将两个属性写为数值类型,不使用length
{ingredients} Ingredients
{steps} Steps
);
}
})
Ⅲ. 默认属性 Default Props
默认值是通过方法 getDefaultProps, 返回一个对象包含默认值
const Summary = createClass({
displayName: "Summary",
// 属性验证
propTypes: {
ingredients: PropTypes.number.isRequired,
steps: PropTypes.isRequired,
title: PropTypes.string
},
// 返回默认值
getDefaultProps() {
return {
ingredients: 0,
steps: 0,
title: "[recipe]"
}
},
render() {
const {ingredients, steps, title} = this.props;
return (
{title}
{ingredients} Ingredients
{steps} Steps
);
}
})
这样即使render组件时,没有添加属性也不会报错
Ⅳ.自定义属性验证(custom property validation)
可以自定义逻辑,React通过一个函数,函数有2个参数 props
, propName
(when rendering component, React will inject the props object and the name of the current property into the function as the arguments),下面只写验证部分
propTypes: {
ingredients: PropTypes.number.isRequired,
steps: PropTypes.isRequired,
// 自定义属性验证
title: (props, propName) =>
(typeof props[propName] !== "string") ?
new Error("a title must be a string") :
(props[propName].length > 20) ?
new Error("title length over 20 characters") :
null
}
// 这个自定义属性验证是指: title是否为一个字符串类型,否:抛出错误,是: 验证长度是否超过20个字符
// {是: 抛出错误, 否: 不执行其他操作}
2.ES6 class创建组件
上面的内置验证, 默认值, 自定义属性验证写法有些微的差异
import React, { Componet, PropTypes} from "react";
class Summary extends Component {
render() {
const {ingredients, steps, title} = this.props;
return (
{title}
{ingredients} Ingredients
{steps} Steps
);
}
}
内置验证 propTypes
, 默认值 defaultProps
写在class外面
Summary.propTypes = {
ingredients: PropTypes.number.isRequired,
steps: PropTypes.isRequired,
// 自定义属性验证
title: (props, propName) =>
(typeof props[propName] !== "string") ?
new Error("a title must be a string") :
(props[propName].length > 20) ?
new Error("title length over 20 characters") :
null
}
Summary.defaultProps = {
ingredients: 0,
steps: 0,
title: "[recipe]"
}
另外一种写法通过static的写法,还在提议阶段,babel不能转换,但是以后可能成为正式规范
class Summary extends Component {
static propTypes = {
ingredients: PropTypes.number.isRequired,
steps: PropTypes.isRequired,
// 自定义属性验证
title: (props, propName) =>
(typeof props[propName] !== "string") ?
new Error("a title must be a string") :
(props[propName].length > 20) ?
new Error("title length over 20 characters") :
null
}, // 注意添加逗号
static defaultProps = {
ingredients: 0,
steps: 0,
title: "[recipe]"
}, // 注意添加逗号
render() {
const {ingredients, steps, title} = this.props;
return (
{title}
{ingredients} Ingredients
{steps} Steps
);
} // 没有逗号
}
3.stateless functional component的写法
import {PropTypes} from "react";
// 对象解构
const Summary = ({ ingredents, steps, title }) => {
return (
{title}
{ingredients} Ingredients
{steps} Steps
)
}
// 属性验证
Summary.propTypes = {
ingredients: PropTypes.number.isRequired,
steps: PropTypes.number.isRequired,
title: PropTypes.string
}
// 默认属性
Summary.defaultProps = {
ingredients: 0,
steps: 0,
title: "[recipe]"
}
另外函数参数解构可以直接添加默认值,上面可以写为
import {PropTypes} from "react";
// 对象解构
const Summary = ({ ingredents=0, steps=0, title="[recipe]" }) => {
return (
{title}
{ingredients} Ingredients
{steps} Steps
)
}
// 属性验证
Summary.propTypes = {
ingredients: PropTypes.number.isRequired,
steps: PropTypes.number.isRequired,
title: PropTypes.string
}
二.refs对象
References 或者 refs 允许React组件和子元素进行交互,最常见的一种使用情形就是: 收集用户输入,组件子元素做出相应的UI渲染
例如获取input中的值:
import React, { Component, PropTypes } from "react";
class AddColorForm extends Component {
constructor(props) {
super(props);
// 绑定组件作用域
this.submit = this.submit.bind(this);
}
submit(e) {
// _title, _color分别指向1个子元素
const { _title, _color } = this.refs;
e.preventDefault();
// this.props表示组件上的属性
this.props.onNewColor(_title.value, _color.value);
—title.value="";
_color.value="#000000";
_title.focus();
}
render() {
return (
)
}
}
// 同样 可以给组件添加属性验证和默认值
AddColorForm.propTypes = {
onNewColor: PropTypes.func
}
AddColorForm.defaultProps = {
onNewColor: f=>f // 表示一个空的函数
}
注意:
- 通过ES6创建的组件,我们必须对需要访问
this
作用域的方法绑定组件的作用域 - 通过 React.createClass()方法创建的组件,不需要给组件绑定 this 作用域,React.createClass将会自动绑定
- 给组件想要引用的子元素添加
ref
字段
渲染时:
{
console.log(`todo: add new ${title} and ${color} to the list`)
}} />
无状态函数组件中使用 refs
不存在this,因此不可能使用this.refs,通过回调函数的方式设置refs,而不是字符串形式
const AddColorForm = ({onNewColor=f=>f}) => {
let _title, _color;
const submit = e => {
e.preventDefault();
onNewColor(_title.value, _color.value);
_title.value = "";
_color.value = "#000000";
_title.focus();
}
return (
)
}
三.state
上面使用属性来处理数据,但是Properties are immutable,一旦渲染就不能改变, state是内置机制来操作数据,当state改变,UI重新渲染;
用户与组件交互有: navigate
, search
, filter
, select
, update
, delete
在React中, UI是应用状态的反射(In React, UI is a reflection of application state).
介绍组件状态
StarRating组件: 2个重要数据,Star总数,被选择的数量
1.Star组件
import React, { PropTypes } from "react";
const Star = ({ selected=false, onClick=f=>f }) =>
Star.propTypes = {
selected: PropTypes.bool,
onClick: PropTypes.func
}
无状态函数组件不能使用state,但是可以成为复杂的,状态多变的组件的子元素,尽可能用无状态组件
同样我们可以通过ES6 class语法表示上面的组件
class Star extends React.Component {
render() {
const {selected, onClick} = this.props
}
}
Star.propTypes = {
selected: PropTypes.bool,
onClick: PropTypes.func
}
Star.defaultProps = {
selected: false,
onClick: f=>f
}
2.StarRating组件
使用React.createClass来创建组件, 通过 getInitialState()
来初始化 state
import React, { PropTypes, createClass } from "react";
const StarRating = createClass({
displayName: "StarRating",
propTypes: {
// 星星的总数
totalStars: PropTypes.number;
},
getDefaultProps() {
return {
totalStars: 5
}
},
// 获取初始状态
getInitialState() {
return {
// 状态变量(state variable)初始化
starSelected: 0
}
},
change(starsSelected) {
// 更新状态变量
this.setState({starsSelected});
},
render() {
const { totalStars } = this.props;
const { starsSelected } = this.state; // 解构状态变量
return (
// 创建长度为totalStars的数组
{[...Array(totalStars)].map((n, i) =>
this.change(i+1)}
/>
)}
{starsSelected} of {totalStars} stars
)
}
})
同样下面通过ES6 class 写法
this.state初始化可以直接写在构造器中
import React, {Component, PropTypes} from "react";
class StarRating extends Component {
constructor(props) {
super(props);
this.state = {
starsSelected: 0
}
this.onChange = this.onChange.bind(this);
}
change(starsSelected) {
this.setState({starsSelected});
}
render() {
const { totalStars } = this.props;
const { starsSelected } = this.state;
return (
// 创建长度为totalStars的数组
{[...Array(totalStars)].map((n, i) =>
this.change(i+1)}
/>
)}
{starsSelected} of {totalStars} stars
)
}
}
StarRating.propTypes = {
totalStars: PropTypes.number
}
StarRating.defaultProps = {
totalStars: 5
}
3.从属性初始化状态componentWillMount()
componentWillMount()
属于 组件生命周期(Component lifecycle),多用于组件在多个应用中重用,这样允许我们直接在主键添加属性进行初始化状态:
render(
,
document.body
);
1.React.createClass写法
const StarRating = React.createClass({
//...
// 从属性更新状态
componentWillMount() {
const { starsSelected } = this.state; // state variable
if (starsSelected) {
this.setState({ starsSelected });
}
},
// ...
})
2.ES6 class 写法: 直接在构造器中写入
class StarRating extends Component {
constructor(props) {
super(props);
this.state = {
// 直接在此处添加
starsSelected: props.starSelected || 0
}
this.onChange = this.onChange.bind(this);
}
// ...
}
总结
通过这章的学习应当掌握以下几点:
- 默认属性验证,默认值,自定义属性验证在3种创建组件方式的差异性
- React.createClass(): 通过
propTypes
对象中规定变量的React.PropTypes
;通过getDefaultProps()
方法返回对象来设置默认值; 通过函数(包含props
和propName
两个属性)来自定义验证逻辑 - ES6 class: 通过在class外部添加属性
propTypes
,defaultProps
分别添加属性类型及默认值,自定义属性与上面一致 - stateless functional component: 与ES6一致,但是默认值可以通过参数对象解构的方式直接添加到参数中
-
isRequired
属性表明属性为必需,这个添加到自定义属性验证中
- React.createClass(): 通过
- refs对象用来表示引用子元素
- React.createClass(),ES6 class中直接在子元素中添加
ref字段,字段为一个字符串类型
比如 - stateless functional component中
通过函数返回对象实例
的方式来引用元素,比如 txtInput = input } />
- React.createClass(),ES6 class中直接在子元素中添加
- state初始化, 关键词:
state variable
|this.setState({stateVaribles})
|this.state
- React.createClass()通过
getInitialState()
的方法来设置state variables
- ES6 class 直接在构造器中添加
this.state
属性来初始化状态变量 - stateless functional components正如其名,没有状态的设置
- React.createClass()通过
- 初步接触 component lifeCycle 方法
componentWillMount()
来通过组件属性来组件默认状态