React消息传递

版权声明: https://blog.csdn.net/weixin_41826907/article/details/79764668

React消息传递
在React中,数据流动是单向的(单向数据流),父组件通过props将数据传递到子组件,子组件根据父组件传递来的属性和组件内部状态来确定如何渲染。因为单向数据流的特性,父组件向子组件传递数据是很容易的,即通过props传递数据,且子组件不能修改自己的props;父组件向孙子组件或者后代组件传递数据同样可以利用props进行层层传递。但是,开发过程中我们会遇到各种各样的情况,譬如子组件向父组件传递消息、兄弟组件之间传递消息等,下面我们看一下如何进行组件之间的消息传递。

父子组件之间进行消息传递
React是单向数据流的,父组件向子组件传递消息可以通过props进行传递:

class Child extends React.Component {
    // 给value设置默认值
    static defaultProps={
        value: '这里是value的默认值'
    }
    // 对value进行类型检查
    static propTypes={
        value: propTypes.string
    }
    render() {
        console.log(this.props.value);  // 123
        return (
            <div>
                {this.props.value}
            </div>
        );
    }
}

class Parent extends React.Component {
    render() {
        return (
            <Child value="123" />
        );
    }
}

ReactDOM.render(<Parent />, document.getElementById('react-root'));

子组件向父组件传递消息也是通过props,因为在JavaScript中函数是一等公民,函数本身既可以像其他对象一样作为prop被传递到子组件,也可以在子组件中被直接调用。因此,我们可以向子组件传递一个callback,子组件通过调用这个callback来向父组件中传递数据。

class Child extends React.Component {
    render() {
        console.log(this.props.value);
        return (
            <input onChange={this.props.handleChange}/>
        );
    }
}

class Parent extends React.Component {
    render() {
        return (
            <Child handleChange={(e) => {console.log(e.target.value)}} />
        );
    }
}

ReactDOM.render(<Parent/>, document.getElementById('react-root'));

兄弟组件之间进行消息传递
兄弟组件不能直接通过props进行消息传递,但是兄弟组件有相同的父元素,因此可以将需要传递的数据挂载在父组件中,由两个兄弟组件共享。父组件通过props将数据传递给两个子组件,如果某个组件需要改变数据并通知其兄弟组件,则通过父组件传递callback给子组件来实现。

class Panel extends React.Component {
    render() {
        console.log(this.props.value);
        return (
            <div>{this.props.value}</div>
        );
    }
}

class Input extends React.Component {
    render() {
        return (
            <input onChange={this.props.handleChange}/>
        );
    }
}

class Parent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value: ''
        }
    }

    render() {
        return (
            <div>
                <Panel value={this.state.value} />
                <Input handleChange={(e) => {this.setState({value: e.target.value})}} />
            </div>
        );
    }
}

ReactDOM.render(<Parent />, document.getElementById('react-root'));

上面这种方式耦合比较严重,父组件承担了本来与自己无关的功能。每次进行消息传递都会引发父组件不必要的生命周期,甚至影响其他子组件。如果消息传递比较频繁,会造成很大的浪费。

我们可以利用观察者模式(发布-订阅模式)实现兄弟组件之间的消息传递,需要利用eventProxy模块帮助实现消息传递功能:

const eventProxy = new EventProxy();

class Child1 extends React.Component {
    render() {
        return (
            <div>
                <input onChange={(event) => {eventProxy.trigger('msg', event.target.value)}} />
            </div>
        );
    }
}

class Child2 extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            msg: ''
        }
    }
    componentDidMount() {
        // 订阅者,监听并接收消息
        eventProxy.on('msg', (msg) => {this.setState({'msg': msg})});
    }

    render() {
        return (
            <div>
                Message from Child1: {this.state.msg}
            </div>
        );
    } 
}

const Parent = () => (
    <div>
        <Child1 />
        <Child2 />
    </div>
)

ReactDOM.render(<Parent/>, document.getElementById('react-root'));

父组件与后代节点之间进行消息传递
前面讲到父组件和子孙组件通信可以通过props层层传递,但是在一个嵌套多层的组件结构中,如果只有最里层的某个组件需要某个属性,该属性由最外层的组件提供(譬如redux中的store),那么就要求中间的组件帮助层层传递该属性,即便这些中间组件不需要这个属性,也要添加对该属性的支持,这是一个很大的缺陷。

React提供了一个解决方案——Context(上下文),它可以让一个树状组件都能访问一个共同的对象,但是需要上级组件和下级组件的配合。

上级组件需要宣称自己支持context(指定组件的childContextTypes属性),并提供函数getChildContext来返回代表Context的对象;该组件的所有子孙组件只需宣称自己需要这个context,就可以通过this.context访问这个共同的环境对象–context。

class Child extends React.Component {
    render() {
        return (
            <span>
                {this.context.VALUE}
            </span>
        )
    }
}

Child.contextTypes = {
    VALUE: React.PropTypes.number
}

const SubParent = (props) => {
    return (
        <div>
            <p>Title</p>
            {
                React.Children.map(props.children, (child) => {
                    return (
                        <li>
                            {child}
                        </li>
                    )
                })
            }
        </div>
    )
}

class Parent extends React.Component {
    getChildContext() {
        return {
            VALUE: 123
        }
    }

    render() {
        return (
            <div>
                <SubParent>
                    <Child />
                    <Child />
                </SubParent>
            </div>
        );
    }
}

Parent.childContextTypes = {
    VALUE: React.PropTypes.number
}

ReactDOM.render(<Parent />, document.getElementById('react-root'));

Redux
为了能够更加清晰高效地管理React应用的数据,Facebook开源React的同时,也推出了Flux架构。Redux是Flux的一种优化实现,在Flux的”单向数据流”原则之上,Redux附加了另外三个原则:

唯一数据源
状态只读
数据改变只能通过纯函数完成
唯一数据源
唯一数据源指应用的状态数据应该只存储在唯一的Store上(Redux没有禁止创建多个Store,而是认为多个Store不会带来好处)。

保持状态只读
保持状态只读指不能直接修改状态,而是通过派发action的方式修改,这一点与Flux类似。

数据改变只能通过纯函数完成
纯函数指Reducer:

reducer(state, action)
reducer根据state和action的值产生一个新对象,作为新的state(对应模块下的新state),而不是直接修改原来的state。reducer只负责计算新状态,而不负责存储状态。

const reducer = (state = {}, action) => {
    switch(action.type) {
        case '1': {
            return {
                ...state,
                status: 1
            }
        }
        case '2': {
            return {
                ...state,
                status: 2
            }
        }
        case '3': {
            return {
                ...state,
                status: 3
            }
        }
        default: {
            return state
        }
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_41826907/article/details/79764668