react系列知识---10组件间通信

215 阅读3分钟

父组件向子组件通信

父组件通过 props 向子组件传递需要的信息

子组件向父组件通信

在用 React 之前的组件开发模式时,常常需要接收组件运行时的状态,这时我们常用的方法 有以下两种:

  1. 利用回调函数:这是 JavaScript 灵活方便之处,这样就可以拿到运行时状态。
  2. 利用自定义事件机制:这种方法更通用,使用也更广泛。设计组件时,考虑加入事件机 制往往可以达到简化组件 API 的目的。
import React, {Component} from 'react';
class ListItem extends Component {
    static defaultProps = {
        text: '',
        checked: false
    }
    render() {
        return (
            <li>
                <input
                    type="checkbox"
                    checked={this.props.checked}
                    onChange={this.props.onChange}/>
                <span>{this.props.value}</span>
            </li>
        );
    }
}
class List extends Component {
    static defaultProps = {
        list: [],
        handleItemChange: () => {}
    };
    constructor(props) {
        super(props);
        this.state = {
            list: this
                .props
                .list
                .map(entry => ({text: entry.text, checked: entry.checked}))
        };
    }
    onItemChange(entry) {
        const {list} = this.state;
        this.setState({
            list: list.map(prevEntry => ({
                text: prevEntry.text,
                checked: prevEntry.text === entry.text
                    ? !prevEntry.checked
                    : prevEntry.checked
            }))
        });
        this
            .props
            .handleItemChange(entry);
    }
    render() {
        return (
            <div>
                <ul>
                    {this
                        .state
                        .list
                        .map((entry, index) => (<ListItem
                            key={`list-${index}`}
                            value={entry.text}
                            checked={entry.checked}
                            onChange={this
                            .onItemChange
                            .bind(this, entry)}/>))}
                </ul>
            </div>
        );
    }
}

跨级组件通信

在 React 中,我们还可以使用 context 来 实现跨级父子组件间的通信

class ListItem extends Component {
    static contextTypes = {
        color: PropTypes.string
    };
    render() {
        const {value} = this.props;
        return (
            <li style={{
                background: this.context.color
            }}>
                <span>{value}</span>
            </li>
        );
    }
}
class List extends Component {
    static childContextTypes = {
        color: PropTypes.string
    };
    getChildContext() {
        return {color: 'red'};
    }
    render() {
        const {list} = this.props;
        return (
            <div>
                <ListTitle title={title}/>
                <ul>
                    {list.map((entry, index) => (<ListItem key={`list-${index}`} value={entry.text}/>))}
                </ul>
            </div>
        );
    }
}

可以看到,我们并没有给 ListItem 传递 props,而是在父组件中定义了 ChildContext,这样从 这一层开始的子组件都可以拿到定义的 context ,例如这里的 color。

注意:React 官方并不建议大量使用 context ,因为尽管它可以减少逐层传递,但当组件结 构复杂的时候,我们并不知道 context 是从哪里传过来的。 Context 就像一个全局变量一样,而 全局变量正是导致应用走向混乱的罪魁祸首之一,给组件带来了外部依赖的副作用。在大部分情 况下,我们并不推荐使用 context 。使用 context 比较好的场景是真正意义上的全局信息且不会 更改,例如界面主题、用户信息等

没有嵌套关系的组件通信

没有嵌套关系的,那只能通过可以影响全局的一些机制去考虑。刚才讲到的自定义事件机制 不失为一种上佳的方法。

import {EventEmitter} from 'events';
export default new EventEmitter();

然后把 EventEmitter 实例输出到各组件中使用:

import ReactDOM from 'react-dom';
import React, {Component, PropTypes} from 'react';
import emitter from './events';
class ListItem extends Component {
    static defaultProps = {
        checked: false
    }
    constructor(props) {
        super(props);
    }
    render() {
        return (
            <li>
                <input
                    type="checkbox"
                    checked={this.props.checked}
                    onChange={this.props.onChange}/>
                <span>{this.props.value}</span>
            </li>
        );
    }
}
class List extends Component {
    constructor(props) {
        super(props);
        this.state = {
            list: this
                .props
                .list
                .map(entry => ({
                    text: entry.text,
                    checked: entry.checked || false
                }))
        };
    }
    onItemChange(entry) {
        const {list} = this.state;
        this.setState({
            list: list.map(prevEntry => ({
                text: prevEntry.text,
                checked: prevEntry.text === entry.text
                    ? !prevEntry.checked
                    : prevEntry.checked
            }))
        });
        emitter.emit('ItemChange', entry);
    }
    render() {
        return (
            <div>
                <ul>
                    {this
                        .state
                        .list
                        .map((entry, index) => (<ListItem
                            key={`list-${index}`}
                            value={entry.text}
                            checked={entry.checked}
                            onChange={this
                            .onItemChange
                            .bind(this, entry)}/>))}
                </ul>
            </div>
        );
    }
}
class App extends Component {
    componentDidMount() {
        this.itemChange = emitter.on('ItemChange', (data) => {
            console.log(data);
        });
    }
    componentWillUnmount() {
        emitter.removeListener(this.itemChange);
    }
    render() {
        return (<List
            list={[
            {
                text: 1
            }, {
                text: 2
            }
        ]}/>);
    }
}

一般情况下,组件之间的通信尽可能保持简洁。如果说程序中出现多级传递或跨级传递时, 那么首先要重新审视一下是否有更合理的方式。Pub/Sub 模式实现的过程非常容易理解,即利用 全局对象来保存事件,用广播的方式去处理事件。这种常规的设计方法在软件开发中处处可见, 但这种模式带来的问题就是逻辑关系混乱。

在上述几种通信模式中,跨级通信往往是反模式的典型案例。对于应用开发来说,应该尽力 避免仅仅通过例如 Pub/Sub 实现的设计思路,加入强依赖与约定来进一步梳理流程是更好的方 法