javascript - React.js - 兄弟组件之间的通信

标签 javascript reactjs flux

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

首先,我将描述任务:

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

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

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

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 有帮助吗?

对于这个问题的一般性质,我深表歉意,但我没有找到很多关于如何以单向方式处理这些同级到同级组件交互的方向。

感谢您的帮助。

最佳答案

TLDR:是的,您应该使用 props-from-top-to-bottom 和 change-handlers-from-bottom-to-top 方法。但这在较大的应用程序中可能会变得笨拙,因此您可以使用 Flux 或 Redux 等设计模式来降低复杂性。

简单的 React 方法

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

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

您在一个 Prop 中传递初始值;和另一个 Prop 中的更改处理程序。

谁可以向组件传递值和更改处理程序?只有他们的 parent 。 (好吧,有一个异常(exception):您可以使用上下文在组件之间共享信息,但这是一个更高级的概念,将在下一个示例中使用。)

因此,无论如何,管理您选择的输入的是您选择的父组件。这是一个例子:

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>)
    }

}

终极版

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

示例(只是您的 redux 应用程序的一些关键部分 - 请参阅 react-redux 文档如何将它们连接在一起,例如 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 示例中的逻辑与普通 React 代码相同。但它不包含在父组件中,而是包含在 reducer 和辅助函数(选择器)中。 React-Redux 不是自上而下传递 props,而是将每个单独的组件连接到状态,从而产生更简单、更模块化、更易于维护的代码。

关于javascript - React.js - 兄弟组件之间的通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36143767/

相关文章:

java - servlet可以更改另一个域的cookie吗

javascript - 为什么两个示例的 Javascript Boolean 输出不同?

javascript - 如何将 setTimeout 与 Loader 一起使用

javascript - Flux 中 ActionType 有什么用?

javascript - 用过去时命名 Flux Action 是一种好习惯吗?

javascript - 更改运行时内置的特定 div 的字体大小

javascript - jquery:无法取消绑定(bind)悬停事件?

javascript - Flummox 和 react 路由器示例

javascript - React - 从数组中删除项目而不是重新渲染列表

c# - React Axios - C# WebAPI 请求 token 在没有服务器错误的情况下失败