React.js - 在兄弟组件之间进行通信 [英] React.js - Communicating between sibling components

查看:131
本文介绍了React.js - 在兄弟组件之间进行通信的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是React的新手,我想问一个关于如何最好地完成必须在兄弟组件之间传递数据的任务的策略问题。

I'm new to React, and I'd like to ask a strategy question about how best to accomplish a task where data must be communicated between sibling components.

首先,我将描述任务:

假设我有多个< select> 子组件由数组组成的单个父级动态传递选择框的单个父级。每个框在其初始状态下具有完全相同的可用选项,但是一旦用户在一个框中选择特定选项,它必须在所有其他框中作为选项禁用,直到它被释放。

Say I have multiple <select> components that are children of a single parent that passes down the select boxes dynamically, composed from an array. Each box has exactly the same available options in its initial state, but once a user selects a particular option in one box, it must be disabled as an option in all other boxes until it is released.

以下是(愚蠢)代码中相同的示例。 (我使用 react-select 作为创建选择框的简写。)

Here's an example of the same in (silly) code. (I'm using react-select as a shorthand for creating the select boxes.)

在此示例中,当用户在一个选择框中选择它们时,我需要禁用(即设置禁用:true )这是我最喜欢的和这是我最不喜欢的选项(如果用户取消选择它们,则释放它们。)

In this example, I need to disable (ie, set disabled: true) the options for "It's my favorite" and "It's my least favorite" when a user selects them in one select box (and release them if a user de-selects them).

var React = require('react');
var Select = require('react-select');



var AnForm = React.createClass({

    render: function(){


        // this.props.fruits is an array passed in that looks like:
        // ['apples', 'bananas', 'cherries','watermelon','oranges']
        var selects = this.props.fruits.map(function(fruit, i) {

            var options = [
                { value: 'first', label: 'It\'s my favorite', disabled: false },
                { value: 'second', label: 'I\'m OK with it', disabled: false },
                { value: 'third', label: 'It\'s my least favorite', disabled: false }
            ];


            return (
                <Child fruit={fruit} key={i} options={options} />
            );
        });


        return (
            <div id="myFormThingy">
                {fruitSelects}
            </div>
        )
    }

});


var AnChild = React.createClass({

    getInitialState: function() {
        return {
            value:'',
            options: this.props.options
        };
    },

    render: function(){

        function changeValue(value){
            this.setState({value:value});
        }


        return (
            <label for={this.props.fruit}>{this.props.fruit}</label>
            <Select
                name={this.props.fruit}
                value={this.state.value}
                options={this.state.options}
                onChange={changeValue.bind(this)}
                placeholder="Choose one"
            />
        )
    }
});

更新子选项是通过回调将数据传递回父级来实现的吗?我应该使用refs来访问该回调中的子组件吗? redux reducer有帮助吗?

Is updating the child options best accomplished by passing data back up to the parent through a callback? Should I use refs to access the child components in that callback? Does a redux reducer help?

我为这个问题的一般性质道歉,但我没有找到很多关于如何处理这些兄弟的方向 - - 以单向方式进行组件交互。

I apologize for the general nature of the question, but I'm not finding a lot of direction on how to deal with these sibling-to-sibling component interactions in a unidirectional way.

感谢您的帮助。

推荐答案

TLDR:是的,你应该使用从顶部到底部的道具和从底部到顶部的变换处理程序。但是在较大的应用程序中这可能会变得难以处理,因此您可以使用Flux或Redux等设计模式来降低复杂性。

TLDR: Yes, you should use a props-from-top-to-bottom and change-handlers-from-bottom-to-top approach. But this can get unwieldy in a larger application, so you can use design patterns like Flux or Redux to reduce your complexity.

简单的React方法

React组件接收其输入作为道具;他们通过调用作为道具传递给他们的函数来传达他们的输出。一个典型的例子:

React components receive their "inputs" as props; and they communicate their "output" by calling functions that were passed to them as props. A canonical example:

<input value={value} onChange={changeHandler}>

您在一个道具中传递初始值;另一个道具中的变更处理程序。

You pass the initial value in one prop; and a change handler in another prop.

谁可以传递值并将处理程序更改为组件?只有他们的父母。 (嗯,有一个例外:您可以使用上下文在组件之间共享信息,但这是一个更高级的概念,并将在下一个示例中使用。)

Who can pass values and change handlers to a component? Only their parent. (Well, there is an exception: you can use the context to share information between components, but that's a more advanced concept, and will be leveraged in the next example.)

因此,在任何情况下,它都是您选择的父组件,应该管理您的选择的输入。下面是一个示例:

So, in any case, it's the parent component of your selects that should manage the input for your selects. Here is an example:

class Example extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            // keep track of what is selected in each select
            selected: [ null, null, null ] 
        };
    }

    changeValue(index, value) {
        // update selected option
        this.setState({ selected: this.state.selected.map((v, i) => i === index ? value : v)})
    }

    getOptionList(index) {
        // return a list of options, with anything selected in the other controls disabled
        return this.props.options.map(({value, label}) => {
            const selectedIndex = this.state.selected.indexOf(value);
            const disabled = selectedIndex >= 0 && selectedIndex !== index;
            return {value, label, disabled};
        });
    }

    render() {
        return (<div>
            <Select value={this.state.selected[0]} options={this.getOptionList(0)} onChange={v => this.changeValue(0, v)} />
            <Select value={this.state.selected[1]} options={this.getOptionList(1)} onChange={v => this.changeValue(1, v)} />
            <Select value={this.state.selected[2]} options={this.getOptionList(2)} onChange={v => this.changeValue(2, v)} />
        </div>)
    }

}

Redux

主要缺点以上方法是你必须从顶部到底部传递大量信息;随着应用程序的增长,这变得难以管理。 React-Redux利用React的上下文功能使子组件能够直接访问您的Store,从而简化您的体系结构。

The main drawback of the above approach is that you have to pass a lot of information from the top to the bottom; as your application grows, this becomes difficult to manage. React-Redux leverages React's context feature to enable child components to access your Store directly, thus simplifying your architecture.

示例(只是redux应用程序的一些关键部分 - 请参阅react-redux文档如何将这些连接在一起,例如createStore,Provider ...):

Example (just some key pieces of your redux application - see the react-redux documentation how to wire these together, e.g. createStore, Provider...):

// reducer.js

// Your Store is made of two reducers:
// 'dropdowns' manages the current state of your three dropdown;
// 'options' manages the list of available options.

const dropdowns = (state = [null, null, null], action = {}) => {
    switch (action.type) {
        case 'CHANGE_DROPDOWN_VALUE':
            return state.map((v, i) => i === action.index ? action.value : v);
        default:
            return state;
    }
};

const options = (state = [], action = {}) => {
    // reducer code for option list omitted for sake of simplicity
};

// actionCreators.js

export const changeDropdownValue = (index, value) => ({
    type: 'CHANGE_DROPDOWN_VALUE',
    index,
    value
});

// helpers.js

export const selectOptionsForDropdown = (state, index) => {
    return state.options.map(({value, label}) => {
        const selectedIndex = state.dropdowns.indexOf(value);
        const disabled = selectedIndex >= 0 && selectedIndex !== index;
        return {value, label, disabled};
    });    
};

// components.js

import React from 'react';
import { connect } from 'react-redux';
import { changeDropdownValue } from './actionCreators';
import { selectOptionsForDropdown } from './helpers';
import { Select } from './myOtherComponents';

const mapStateToProps = (state, ownProps) => ({
    value: state.dropdowns[ownProps.index],
    options: selectOptionsForDropdown(state, ownProps.index)
}};

const mapDispatchToProps = (dispatch, ownProps) => ({
    onChange: value => dispatch(changeDropdownValue(ownProps.index, value));
});

const ConnectedSelect = connect(mapStateToProps, mapDispatchToProps)(Select);

export const Example = () => (
    <div>
        <ConnectedSelect index={0} />
        <ConnectedSelect index={1} />
        <ConnectedSelect index={2} />
    </div>
);

正如您所看到的,Redux示例中的逻辑与vanilla React代码相同。但它不包含在父组件,但在reducers和helper函数(选择器)中.Ract-Redux不是自上而下传递道具,而是将每个组件连接到状态,从而实现更简单,更模块化,更易于维护的c颂歌。

As you can see, the logic in the Redux example is the same as the vanilla React code. But it is not contained in the parent component, but in reducers and helper functions (selectors). An instead of top-down passing of props, React-Redux connects each individual component to the state, resulting in a simpler, more modular, easier-to-maintain code.

这篇关于React.js - 在兄弟组件之间进行通信的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆