React入门(三)---- 组件通信
React的通信形式和Vue相同,都是存在父子组件通信,子父组件通信和非父子组件通信。
所以,我们可以按照之前的Vue组件通信的思路在React的规则条件下加以理解,React只允许自己组件内修改状态,所以,我们要将React的状态操作放入组件本身,哪怕不是这个组件触发的修改事件。
父子组件通信
父子组件通信,简单地可以理解为传参,通过自带的props属性传递参数实现父子组件通信。
// 父组件传递数组给子组件
constructor (props) {
super (props)
this.state = {
list:[
{
id:1,
task:'good good study'
},
{
id:2,
task:'day day up'
}
]
}
}
render () {
return (
<Fragment>
<div className="container">
<HeaderBox addTask = {this.addTask} />
</div>
<div className="container">
<Content listData={this.state.list} />
</div>
</Fragment>
)
}
// 子组件接受数据并渲染
renderItem = () => {
const { listData } = this.props
return listData.map( (item,index) => <List index = { index } key = { item.id } item = { item.task } /> )
}
render() {
return (
<Fragment>
<ul className="list-group">
{ this.renderItem() }
</ul>
</Fragment>
)
}
子父组件通信
React的子父组件通信类似于父子组件通信,将父组件的方法作为属性传入子组件的props,在子组件内事件驱动父组件的方法,实现子父组件通信。
// 父组件方法传递至子组件
addTask = (val) => {
let {list} = this.state
const {listSort} =utils
console.log(listSort)
this.setState(() => {
list.push({
id:listSort(list)[0].id + 1,
task:val
})
return {
list
}
})
}
render () {
return (
<Fragment>
<div className="container">
<HeaderBox addTask = {this.addTask} />
</div>
<div className="container">
<Content/>
</div>
</Fragment>
)
}
// 子组件事件驱动父组件方法
add = (e) => {
if ( e.keyCode == 13 ){
this.props.addTask(e.target.value)
}
}
render () {
return (
<Fragment>
<header style={{textAlign:'center',fontSize:'22px',margin:'10px auto'}} > Todo List </header>
<input type="task" className="form-control" onKeyUp = { this.add.bind(this) } id="exampleInputEmail1" placeholder="请输入新的任务"/>
</Fragment>
)
}
非父子组件通信
非父子组件通信在React内其实都是通过同一父或者祖辈组件,作为数据沟通的桥梁(当一个组件为另一个父组件或是祖辈组件时,也认为是存在同一父组件或祖辈组件),存在两种路径,一种是通过ref链传递数据,另一种是context传递数据。
ref链非父子组件通信
ref链可以在父组件获取子组件的数据,所以我们的思路就很简单,在一个子组件内定义方法,通过ref链在父组件内获取子组件的方法,然后再传递给另一个子组件,通过另一个子组件的事件触发上一个子组件的方法,实现两个兄弟组件的数据共通,但其实这种方式并不实用,在正经的项目内,所有共同需要调用的方法和数据应该存放在共同的父组件内,所以我们并不需要从一个子组件传递到另一个子组件,而是通过父组件统一分发即可。
父组件内,子组件ref的定义方式:
// 两种定义方式
<Son ref = "son" />
<Son ref = { el => this.son = el }/>
// 第一种调用方式
kick = () => {
this.refs.son.changeFlag()
}
// 第二种调用方式
kick = () => {
this.son.changeFlag()
}
// 两种定义方式的调用形式不同,所以推荐第二种方式,因为调用简单。
context的非父子组件通信方式
context和Vue的bus总线有点类似,就是讲数据挂在一个能被多种不同关系的组件访问的地方,实现费父子组件通信。
但是两者存在的区别也很明显,bus是可供所有的组件访问,而context只作用于自己的结构树下方的组件。
import React, { Component,createContext } from 'react'
// const MoneyContext = createContext( 默认值 )
const MoneyContext = createContext( 0 )
class Father extends Component{
render () {
return (
<div>
father
<Son/>
</div>
)
}
}
class Son extends Component{
static contextType = MoneyContext
render () {
return (
<div>
son
<p> 爷爷给了我 { this.context } </p>
</div>
)
}
}
export default class Stride extends Component {
constructor(props) {
super(props)
this.state = {
money: 100000
}
}
render() {
const { money } = this.state
return (
<div>
<h3> 爷爷 </h3>
<MoneyContext.Provider value = { money }>
<Father/>
</MoneyContext.Provider>
</div>
)
}
}
context需要引入createContext,然后接收数据的组件一定要在父组件的context标签内,接受数据的子孙组件需要静态指定自己的contextType才可以在this.context访问到数据。