前言:之前的文章中我们快速撸过一遍ES6语法和jsx语法,这些都是为了开发react组件所做的准备,在本节中,将真正进入react开发实践当中。
1. 组件component
根据react官网对组件的描述是这样:组件就像JavaScript函数,它们接受任意输入(称为“props”)并返回描述屏幕上应显示内容的React元素。在react中,我们可以认为,一切都是一个组件,即使是纯HTML标签也是它们自己的组件。
普通组件
在react中,我们有两种方式来创建组件:
- 函数式组件:
import React from 'react'
const ListItem = () => {
return (
<div>
<h1>Title</h1>
<p>Description</p>
</div>
)
}
- 通过class创建组件
import React, { Component } from 'react'
class ListItem extends Component {
render() {
return (
<div>
<h1>Title</h1>
<p>Description</p>
</div>
)
}
}
在React Hooks出现之前,只有通过class来创建的组件才有自己的状态,可以访问react中的生命周期方法,比如componentDidMount(),shouldComponentUpdate()等等,以便在首次呈现,更新或删除组件时执行操作。而通过函数生命的组件又称为无状态组件,没有组件的生命周期方法。React Hooks的出现改变了这一点,我们的功能组件现在比以往任何时候都强大得多,但是目前class仍然是创建组件的完美有效方式。
2. 状态(state)
在react中,我们可以通过设置,改写state来进行与组件状态的交互。
设置默认的state
在Component的构造函数中,初始化this.state。例如,ListItem组件:
class ListItem extends Component {
constructor(props) {
super(props)
this.state = {
hasClicked: false
}
}
render() {
return (
<div>
<h1>Title</h1>
<p>Description</p>
</div>
)
}
}
获取state
我们可以通过this.state.hasClicked来获取hasClicked的值
class ListItem extends Component {
constructor(props) {
super(props)
this.state = {
hasClicked: false
}
}
render() {
return (
<div>
<h1>Title</h1>
<p>Description</p>
<p>Clicked: {this.state.hasClicked}</p>
</div>
)
}
}
改变state
react中,我们不能通过直接修改的方式来赋值:
// 无效的方式
this.state.hasClicked = false
而是通过给setState()方法传递一个对象的方式来实现对state的修改:
// 有效的方式
this.setState({ hasClicked: false })
这个传递的对象可以是state的一个子集,也可是一个state的超集,只有你传递的属性才会被修改,省略的属性将保持不变。当我们调用setState方法后,react就会知道state已经改变了,然后会调用一系列的方法,导致组件的刷新和DOM更新。
单向数据流
state始终只由一个组件拥有,更改一个state的值,只会影响这个组件或是这个组件的子组件,这也是为什么state会经常在组件树中向上移的原因。举个例子,如果两个组件需要共享状态,我们就需要把这个共享状态移动到它们共同的祖先组件里,通过props来传递值,我们可以通过在祖先组件中定义一个方法,再通过props传递到子组件中来调用,以达到两个组件都能共享和修改这个共享的state的目的。
const ChildA = ({count}) => {
return (
<div>count: {count}</div>
)
}
const ChildB = ({add}) => {
return (
<div onClick={this.add}>加1</div>
)
}
class FatherComp extend Component {
constructor(props){
super(props){
this.state = {
count:0
}
}
}
add = () => {
this.setState({
count: this.state.count+1
})
}
render(){
return (
<div>
<ChildA count={this.state.count} />
<ChildB add={this.add} />
</div>
)
}
}
3. props
在react中,我们通过props进行父子组件间的传值,每个子组件都从父组件获取其props。
在一个函数组件中,props就是它传递的全部内容,并且可以通过添加props作为函数参数来获取它们:
const ListItem = props => {
return (
<div>
<h1>{props.title}</h1>
<p>{props.description}</p>
</div>
)
}
在类组件中,props默认就被传递,因此,我们没有必要去额外做些什么,我们可以通过this.props来获取:
import React, { Component } from 'react'
class ListItem extends Component {
render() {
return (
<div>
<h1>{this.props.title}</h1>
<p>{this.props.description}</p>
</div>
)
}
}
传递props是父组件向子组件传值的最好方法,子组件可以通过其props保存数据(有state)或接收数据。
但是props也有其局限性:
- props有可能通过好几级的组件传递进来,不方便使用
- 你有可能需要去访问一个完全不相关的组件的state,这种情况用props很难实现
props默认值
在初始化时候,如果我们没有任何值能够获取到,我们需要定义一个默认值,否则这个props值就会丢失掉:
ListItem.propTypes = {
title: PropTypes.string,
description: PropTypes.string
}
ListItem.defaultProps = {
title: '',
description: ''
}
props的传递
初始化组件时,以类似于HTML属性的方式传递props:
const desc = 'A description'
//...
<ListItem title="A blog post" description={desc} />
这个例子中,我们将title作为string传递,并将description作为desc这个变量来传递。
children
children是一个特殊的props,它包含了组件body中传递的任何值:
<ListItem title="A blog post" description="{desc}">
Something
</ListItem>
例子中,在ListItem组件里,我们可以通过this.props.children来获取到“Something”。
propTypes
由于JavaScript是一种动态类型语言,我们没有办法在编译时强制定义变量的类型,因此如果我们传递一个无效类型,在运行时可能会报错或者产生不是我们期望的结果。
避免这种方式的一种方法是使用TypeScript,但React有一种直接对props类型有帮助的方法,可以在运行代码时帮助我们检测出传递错误的值:
import PropTypes from 'prop-types'
import React from 'react'
class ListItem extends Component {
render() {
return (
<div>
<h1>{this.props.title}</h1>
<p>{this.props.description}</p>
</div>
)
}
}
ListItem.propTypes = {
title: PropTypes.string,
description: PropTypes.string
}
export default ListItem
下面是一些我们能够设置的基本类型:
- PropTypes.array
- PropTypes.bool
- PropTypes.func
- PropTypes.number
- PropTypes.object
- PropTypes.string
- PropTypes.symbol
网友评论