组件间通信的4种情况
- 父组件向子组件通信
- 子组件向父组件通信
- 跨级组件通信
- 没有嵌套关系组件之间的通信
父组件向子组件通信
父组件向自组件传递信息是最常见的,父组件通过props
向自组件传递需要的信息。
import React, { Component } from 'react';
import Child from './Child';
export default class Parent extends Component {
render() {
return (
<div>
<Child name="Sara" />
</div>
);
}
}
/子组件
import React from 'react';
export default function Child({ name }) { // 解构赋值
return <h1>Hello, {name}</h1>
}
不常见的方法:ref
利用ref对子组件进行标记。在父组件中直接调用子组件的方法实现通信。不推荐使用。
var Father = React.createClass({
hitSon(){
// this.refs.son.setState({doing : '学习'})
this.refs.son.goToStudy() //通过refs调用子组件的方法
},
render:function () {
return (
<div>
父组件:
<button onClick={this.hitSon}>go</button>
<hr/>
<Son ref='son'/>
</div>
)
}
})
var Son = React.createClass({
getInitialState(){
return {
doing:'玩游戏'
}
},
goToStudy(){
this.setState({doing:'学习'})
},
render:function () {
return (
<div>
子组件:
<p>正在{this.state.doing}...</p>
</div>
)
}
})
ReactDOM.render(<Father/>,document.getElementById("app"))
子组件向父组件传递信息
- 子组件更新组件状态,通过回调函数的方式传递给父组件。
子组件调用父组件通过props传给它的函数更新父组件state,进而完成子组件向父组件的通讯。 - 利用自定义事件机制
利用回调函数
简单的说就是把父组件的方法通过props传递给子组件,子组件通过执行父组件方法的形式将信息传递给父组件。
import React, { Component } from 'react';
import Child from './child.js';
export default class Parent extends Component {
state = {
msg: 'parent'
}
callback=(msg)=>{
this.setState({msg});
}
render() {
return (
<div>
<Child callback={this.callback} ></Child>
</div>
);
}
}
import React from "react";
export default class Child extends React.Component{
state={
msg: "Child"
}
render(){
return(
<div>
<button onClick={()=>this.props.callback(this.state.msg)}>go</button>
</div>
)
}
}
跨级组件间通信
- 层层组件传递props
- 使用context
层层组件传递props {…this.props}
同 props
context
在react没有类似vue中的事件总线来解决这个问题,我们只能借助它们共同的父级组件来实现,将非父子关系装换成多维度的父子关系。react提供了context
api来实现跨组件通信, React 16.3之后的context
api较之前的好用。
- context是一个全局变量,像是一个大容器,在任何地方都可以访问到,我们可以把要通信的信息放在context上,然后在其他组件中可以随意取到;
- 但是React官方不建议使用大量context,尽管他可以减少逐层传递,但是当组件结构复杂的时候,我们并不知道context是从哪里传过来的;
- 实例,使用
context
实现购物车中的加减功能
// counterContext.js
import React, { Component, createContext } from 'react' //第一步导出context
const {
Provider,
Consumer: CountConsumer // 解构起别名
} = createContext() //解构出Consumer和Provider
class CountProvider extends Component {
constructor () {
super()
this.state = {
count: 1
}
}
increaseCount = () => {
this.setState({
count: this.state.count + 1
})
}
decreaseCount = () => {
this.setState({
count: this.state.count - 1
})
}
render() {
return (
<Provider value={{ // 第三步 使用Provider包裹
count: this.state.count,
increaseCount: this.increaseCount,
decreaseCount: this.decreaseCount
}}
>
{this.props.children}
</Provider>
)
}
}
export {
CountProvider,
CountConsumer
}
// 定义CountButton组件
const CountButton = (props) => {
return (
<CountConsumer>
// consumer的children必须是一个方法
{
({ increaseCount, decreaseCount }) => {
const { type } = props
const handleClick = type === 'increase' ? increaseCount : decreaseCount
const btnText = type === 'increase' ? '+' : '-'
return <button onClick={handleClick}>{btnText}</button>
}
}
</CountConsumer>
)
}
// 定义count组件,用于显示数量
const Count = (prop) => {
return (
<CountConsumer>
{
({ count }) => {
return <span>{count}</span>
}
}
</CountConsumer>
)
}
// 组合
class App extends Component {
render () {
return (
<CountProvider>
<CountButton type='decrease' />
<Count />
<CountButton type='increase' />
</CountProvider>
)
}
}
复杂的非父子组件通信在react中很难处理,多组件间的数据共享也不好处理,在实际的工作中我们会使用flux、redux、mobx来实现
没有嵌套关系的组件通信
- 使用自定义事件机制,也就是事件抛发,在List1中侦听changeMessage事件,在List2中抛发事件,完毕后在钩子函数中卸载
yarn add events
在src下新建一个util目录里面建一个events.js
import {EventEmitter} from 'events';
export default new EventEmitter();
//list.jsx
import React, { Component } from 'react';
import emitter from '../util/events';
export default class List extends Component {
constructor(props) {
super(props);
this.state = {
message: 'List1',
};
}
componentDidMount() {
// 组件装载完成以后声明一个自定义事件
this.eventEmitter = emitter.addListener('changeMessage', (message) => {
this.setState({
message,
});
});
}
componentWillUnmount() {
emitter.removeListener(this.eventEmitter);
}
render() {
return (
<div>
{this.state.message}
</div>
);
}
}
//list2.jsx
import React, { Component } from 'react';
import emitter from '../util/events';
export default class List2 extends Component {
handleClick = (message) => {
emitter.emit('changeMessage', message);
};
render() {
return (
<div>
<button onClick={this.handleClick.bind(this, 'List2')}>点击我改变List1组件中显示信息</button>
</div>
);
}
}
//APP.jsx
import React, { Component } from 'react';
import List1 from './components/List1';
import List2 from './components/List2';
export default class App extends Component {
render() {
return (
<div>
<List1 />
<List2 />
</div>
);
}
}
redux
redux作为独立于react的数据管理仓库,之后再说。