我是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"
/>
)
}
});
通过将数据通过回调传递回父级来最好地完成子级选项的更新?我应该使用ref来访问该回调中的子组件吗?Redux Reducer有帮助吗?
对于这个问题的一般性质,我深表歉意,但是对于如何以单向方式处理这些同级组件之间的交互,我没有找到很多指导。
谢谢你的帮助。
TLDR:是的,您应该使用从上到下的支持和从下到上的更改处理程序。但这在较大的应用程序中会变得笨拙,因此您可以使用Flux或Redux之类的设计模式来降低复杂性。
简单的React方法
React组件接收其“输入”作为道具;他们通过调用作为道具传递给他们的函数来传达其“输出”。一个典型的例子:
<input value={value} onChange={changeHandler}>
您通过一个道具传递了初始值;和另一个道具中的变更处理程序
谁可以传递值并将更改处理程序传递给组件?只有他们的父母。(嗯,有一个例外:您可以使用上下文在组件之间共享信息,但这是一个更高级的概念,将在下一个示例中使用。)
因此,无论如何,应该由选择的父组件来管理选择的输入。这是一个例子:
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的上下文功能使子组件可以直接访问您的商店,从而简化了您的架构。
示例(仅是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代码相同。但是它不包含在父组件中,而是包含在reduces和helper函数(选择器)中。React-
Redux代替了自上而下传递道具,将每个单独的组件连接到状态,从而产生了更简单,更模块化,更易于维护的代码。
问题内容: 我在页面上有一个组件的两个实例(搜索字段),在它们之间有另一个组件(进行服务器调用的按钮),例如: 我要做的只是将一个未修改的参数从CardSearch字段传递到RunOnServer按钮,但是如果简单的话,我该死的。据此,我可以使用this.state.var作为道具,但是这样做可以在编译代码时给我’undefined.state.var’。React的官方文档并不出色。他们只是告诉
问题内容: 总览 在Vue.js2.x中,将不推荐使用。 那么,在[Vue.js2.x中的]兄弟组件之间进行通信的正确方法是什么? 背景 据我了解Vue 2.x,同级通信的首选方法 是使用商店或事件总线 。 根据Evan(Vue的创建者)的说法: 值得一提的是,“在组件之间传递数据”通常不是一个好主意,因为最终数据流变得不可跟踪且很难调试。 如果一条数据需要由多个组件共享,则首选全局存储或Vuex
我刚开始使用ReactJS,遇到了一个小问题。 我的应用程序本质上是一个带有过滤器的列表和一个更改布局的按钮。目前我使用三个组件:
问题内容: 问题是: 假设我们有两个正在运行的Node.js进程:和。 结果中有返回的函数。 是否有一种从内部调用并获得结果的方法? 从我对Node.js的了解中,我仅找到一种使用套接字进行通信的解决方案。但是,这不是理想的,因为它将需要一个进程在端口上侦听。如果可能,我希望避免这种情况。 编辑: 经过一些问题,我很想补充一点,在层次结构中不能是的子进程,而恰恰相反。同样,如果有帮助,则只能有一个
umijs 3.x react router 5 使用 react-router-scroll-memory ,在路由切换时保存滚动位置。 使用方式是将组件放到这个位置: 但是,umijs 并没有暴露 BrowserRouter、App。如何使用这个组件或者有其他解决方案吗。
问题内容: 我有一个ListComponent。在ListComponent中单击某个项目时,该项目的详细信息应显示在DetailComponent中。两者同时显示在屏幕上,因此不涉及路由。 如何告诉DetailComponent单击ListComponent中的哪个项目? 我考虑过向父对象(AppComponent)发出事件,并让父对象使用@Input在DetailComponent上设置sel