组件
创建组件的方式
- createClass
- 最原始的方法,现在已经在 v16+ 移除
// v16已经移除此方法 const createHeader = React.createClass({ getInitialState: function() { return { name: 'createClass' } }, render: function() { return <div>{this.state.name}:{this.props.title}</div> } }) const title = 'React' ReactDOM.render( <div> <createHeader title={title}/> </div>, document.getElementById('root') ); - Component
- 使用 ES6 的新语法来编写组件类,使用
class关键字继承React.Component来生成一个组件类
class Header extends Component { constructor(props) { super(props) this.state = { name: 'class Component' } } render() { return <div>{this.state.name}:{this.props.title}</div> } } const title = 'React' ReactDOM.render( <div> <Header title={title}/> </div>, document.getElementById('root') ); - 使用 ES6 的新语法来编写组件类,使用
- Function Component
- 一般用于编写
无状态的组件,自身没有状态,依赖于传入的props来决定渲染
const FuncHeader = ({ title }) => { const name = 'Function' return <div>{name}:{title}</div> } const title = 'React' ReactDOM.render( <div> <FuncHeader title={title}/> </div>, document.getElementById('root') ); - 一般用于编写
- PureComponent
- 继承
React.PureComponent,只会对属性和状态进行浅比较, 直接比较是否相等,如果是引用类型,由于浅比较,所以并不会更新组件,有一定的性能优化作用
//普通组件,只要调用 render 无论变化与否,必定更新 class FakeHeader extends Component { constructor(props) { super(props) this.state = { name: 'class Component FakeHeader' } } render() { const { title, data:{ title: dataTitle } } = this.props const { name } = this.state const str = `${name} - dataTitle:${dataTitle} - title:${title}` console.log(`update - ${str}`) return <div>{str}</div> } } //官方 PureComponent 只有在浅比较下数据变化,更新组件 class PureHeader extends PureComponent { constructor(props) { super(props) this.state = { name: 'class PureComponent' } } render() { const { title, data:{ title: dataTitle } } = this.props const { name } = this.state const str = `${name} - dataTitle:${dataTitle} - title:${title}` console.log(`update - ${str}`) return <div>{str}</div> } } //模拟实现 纯组件 class FakePureHeader extends Component { constructor(props) { super(props) this.state = { name: 'class FakePureHeader' } } shouldComponentUpdate(nextProps, nextState){ const { props, state } = this const shallowCompare = (a, b) => a === b || Object.keys(a).every(k => a[k] === b[k]) // 浅比较数据更新才更新 return !shallowCompare(nextProps, props) || !shallowCompare(nextState, state) } render() { const { title, data:{ title: dataTitle } } = this.props const { name } = this.state const str = `${name} - dataTitle:${dataTitle} - title:${title}` console.log(`update - ${str}`) return <div>{str}</div> } } const title = 'React' const data = { title } const render = (data, title) => { ReactDOM.render( <div> {/* <createHeader title={title}/> */} <Header title={title}/> <FuncHeader title={title}/> <FakeHeader data={data} title={title}/> <PureHeader data={data} title={title}/> <FakePureHeader data={data} title={title}/> </div>, document.getElementById('root') ); } // update - class Component FakeHeader - dataTitle:React - title:React // update - class PureComponent - dataTitle:React - title:React // update - class FakePureHeader - dataTitle:React - title:React render(data, title) setTimeout(() => { data.title = 'update' // update - class Component FakeHeader - dataTitle:update - title:React // 改变的是引用值 PureComponent 和 FakePureHeader 不会更新 render(data, title) }, 1000); setTimeout(() => { // update - class Component FakeHeader - dataTitle:update - title:title // update - class PureComponent - dataTitle:update - title:title // update - class FakePureHeader - dataTitle:update - title:title // 改变的是基本类型,更新组件 render(data, 'title') }, 3000); - 继承
组件生命周期
挂载
- 旧版本
constructor:初始化组件componentWillMount:组件挂载之前render:渲染组件componentDidMount:组件挂载结束
- 新版本
constructorgetDerivedStateFromProps:静态方法!render之前调用,返回一个state更新组件,返回null则不更新,无法获取到实例!!!rendercomponentDidMount
更新
父组件更新
- 旧版本
componentWillReceiveProps:已挂载的组件接收新的props之前被调用shouldComponentUpdate:返回一个Boolean确定是否更新组件componentWillUpdate:组件更新之前调用rendercomponentDidUpdate:组件更新结束后调用
- 新版本
getDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdate:在最近一次渲染输出(提交到 DOM 节点)之前调用。componentDidUpdate
状态更新
- 旧版本
shouldComponentUpdatecomponentWillUpdaterendercomponentDidUpdate
- 新版本
getDerivedStateFromPropsshouldComponentUpdaterendergetSnapshotBeforeUpdatecomponentDidUpdate
卸载
componentWillUnmount:组件卸载时调用
class Header extends Component {
constructor(props) {
console.log('constructor...')
super(props)
this.state = {
name: 'Header'
}
}
static getDerivedStateFromProps(props, state){
console.log('getDerivedStateFromProps...')
return state
}
componentWillMount() {
console.log('componentWillMount...')
}
componentWillReceiveProps(){
console.log('componentWillReceiveProps...')
}
shouldComponentUpdate(nextProps, nextState){
console.log('shouldComponentUpdate...')
return true
}
componentWillUpdate(nextProps, nextState){
console.log('componentWillUpdate...')
console.log(nextProps, nextState)
}
handleClick(){
this.setState({
name: this.state.name + `~`
})
}
render() {
console.log('render...')
return ()
}
getSnapshotBeforeUpdate(prevProps, prevState){
console.log('getSnapshotBeforeUpdate...')
}
componentDidUpdate(prevProps, prevState, snapshot){
console.log('componentDidUpdate...')
}
componentDidMount(){
console.log('componentDidMount...')
}
componentWillUnmount(){
console.log('componentWillUnmount...')
}
}
const title = 'React'
const render = (title) => {
ReactDOM.render(
<div>
{title && <Header title={title}/>}
</div>,
document.getElementById('root')
);
}
// 首次挂载
// constructor
// componentWillMount
// render
// componentDidMount
// 新版本的生命周期
// constructor
// getDerivedStateFromProps
// render
// componentDidMount
render(title)
// 组件内部更新 state
// shouldComponentUpdate
// componentWillUpdate
// render
// componentDidUpdate
// 新版本的生命周期
// getDerivedStateFromProps
// shouldComponentUpdate
// render
// getSnapshotBeforeUpdate
// componentDidUpdate
setTimeout(() => {
// 父组件更新
// componentWillReceiveProps
// shouldComponentUpdate
// componentWillUpdate
// render
// componentDidUpdate
// 新版本的生命周期
// getDerivedStateFromProps
// shouldComponentUpdate
// render
// getSnapshotBeforeUpdate
// componentDidUpdate
render('title')
}, 1000);
setTimeout(() => {
// 移除组件
// componentWillUnmount
render()
}, 3000);
属性 props
属性 props 对于组件来说是只读的,只能又父组件改变属性来更新子组件,数据流是单向的。
children 组件表示当前组件的子组件集合
属性可以通过设置 defaultProps 来做属性的默认值,通过引入 prop-types 来做属性入参的类型限制
状态 state
在 constructor 中初始化,或者直接组件中 state = {}
通过调用 this.setState 来更新状态
setState
- 异步:合成事件和生命周期钩子调用
- 同步:原生事件和
setTimeout调用 - 批量更新:异步执行的时候,会把多次调用合成一次,只执行一次更新
- 函数式调用:
this.setState((state, props) => newState,callback?)
class Header extends Component {
static defaultProps = {
title: 'default'
}
constructor(props) {
super(props)
this.state = {
name: 'Header'
}
}
handleClick(){
this.setState({
name: this.state.name + '~'
})
}
render() {
return <div onClick={e => this.handleClick()}>{this.state.name}:{this.props.title}</div>
}
}
const render = (title) => {
ReactDOM.render(
<div>
<Header/>
<Header title={title}/>
</div>,
document.getElementById('root')
);
}
render('')
setTimeout(() => {
render('title')
}, 1000);
组件和事件
React 事件的特点
- 事件命名是驼峰式命名法,例如
onClick - 事件处理器是一个函数,不是字符串。例如
onClick={this.handleClick} - 声明式,不需要经跟选择器绑定事件
- 事件代理,React 只在根元素上绑定事件,所有的事件响应都通过事件代理响应
- 返回经过封装的 事件对象,兼容性更好,原生的事件对象可以通过
e.nativeEvent访问
class Count extends Component {
constructor(props) {
super(props)
this.state = {
count: 0
}
this.handleClickMinus = this.handleClickMinus.bind(this)
}
handleClickPlus(){
this.setState({
count: this.state.count + 1
})
}
handleClickMinus(){
this.setState({
count: this.state.count - 1
})
}
render() {
const { count } = this.state
return <div>
<span>count:{count}</span>
<button onClick={e => this.handleClickPlus(e)}>+</button>
<button onClick={this.handleClickMinus}>-</button>
</div>
}
}
const render = () => {
ReactDOM.render(
<div>
<Count/>
</div>,
document.getElementById('root')
);
}
render()
组件通信
父子组件
- 父传子
- 通过属性
props来完成<Child title={title}/>
- 通过属性
- 子传父
- 通过回调函数 或者 引入发布订阅消息的类,通过消息发布订阅来实现传递消息
class Child extends Component {
handleClick(){
this.props.onClick('Child')
}
render(){
const { name } = this.props
return <div onClick={e => this.handleClick(e)}>
msg from {name}
</div>
}
}
class Parent extends Component {
constructor(props){
super(props)
this.state = {
name: 'parent'
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(name){
this.setState({
name
})
}
render(){
const { name } = this.state
return <div>
{name}
<Child onClick={this.handleClick} name={name}/>
</div>
}
}
ReactDOM.render(
<div>
<Parent/>
</div>,
document.getElementById('root')
);
爷孙组件
爷孙通信可以通过 React 提供的 context 来通信,一般传递一些只读的信息,类似于全局变量,滥用和容易改变让程序变得不可预测。
class Child extends Component {
// 声明需要读取 context 上的数据
static contextTypes = {
text: PropTypes.string,
changeText: PropTypes.func
}
handleClick(){
this.props.onClick('Child')
//最好不要通过子组件去改变祖先组件的状态,这样会让状态变得不可预测
this.context.changeText('child')
}
render(){
const { name } = this.props
// 获取 context 上的数据
const { text } = this.context
return <div onClick={e => this.handleClick(e)}>
msg from {name} {text}
</div>
}
}
class Parent extends Component {
constructor(props){
super(props)
this.state = {
name: 'parent'
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(name){
this.setState({
name
})
}
render(){
const { name } = this.state
return <div>
{name}
<Child onClick={this.handleClick} name={name}/>
</div>
}
}
class Ancestor extends Component {
constructor(props){
super(props)
this.state = {
name: 'Ancestor'
}
}
// 声明 要在 context 上放的数据
static childContextTypes = {
text: PropTypes.string,
changeText: PropTypes.func
}
handleText(name){
this.setState({
name
})
}
// 祖先组件往 context 放东西
getChildContext(){
return { text: this.state.name, changeText: this.handleText.bind(this) }
}
render(){
const { name } = this.state
const { children } = this.props
return <div>
{name}
{children}
</div>
}
}
const render = () => {
ReactDOM.render(
<div>
<Ancestor>
<Parent/>
</Ancestor>
</div>,
document.getElementById('root')
);
}
render()
兄弟组件
通过共同的父组件作为桥梁实现两个组件的通信
class Count1 extends Component {
handleClick(){
const { sum, onClick } = this.props
onClick(sum + 1)
}
render(){
const { sum } = this.props
return <button onClick={(e)=>this.handleClick(e)}>{sum}</button>
}
}
class Count2 extends Component {
handleClick(){
const { sum, onClick } = this.props
onClick(sum - 1)
}
render(){
const { sum } = this.props
return <button onClick={(e)=>this.handleClick(e)}>{sum}</button>
}
}
class CountBox extends Component {
constructor(props){
super(props)
this.state = {
sum: 0
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(sum){
this.setState({
sum
})
}
render(){
const { sum } = this.state
return <div>
sum:{sum}
<Count1 sum={sum} onClick={this.handleClick}/>
<Count2 sum={sum} onClick={this.handleClick}/>
</div>
}
}
const render = () => {
ReactDOM.render(
<div>
<CountBox/>
</div>,
document.getElementById('root')
);
}
render()
任意组件
一般使用状态管理工具 Redux 来集中统一处理