我正在处理一个数据模型'是一个集合,例如,一群人。它们被打包到React组件中并在页面上平铺。基本上它就像:
class App extends React.Component {
constructor() {
super();
this.state = { people: /* some data */ };
}
render () {
return (
<div>
{this.state.people.map((person) =>
<People data={person}></People>)}
</div>);
}
}
现在我想为<People>
组件中的每个条目附加一个编辑部分,允许用户更新名称,年龄......特定条目的各种信息。
由于React不支持在组件内部改变道具,我搜索并发现添加回调作为道具可以解决将数据传递给父级的问题。但由于有许多字段需要更新,因此会有许多回调,例如onNameChanged
,onEmailChanged
...这可能非常难看(随着字段数量的不断增加,也越来越冗长)。
那么正确的方法是什么?
答案 0 :(得分:1)
诚实? 最佳方式是Flux (在一分钟内回到那里)。
如果您开始以道具的形式向下传递数据,然后将其传回以使用回调进行编辑,那么您将打破React构建的单向数据流
然而,并非所有项目都需要写入理想标准,并且可以在没有Flux的情况下构建它(有时甚至可能是正确的解决方案)。
通过传递单个edit
函数作为道具,您可以在不需要大量回调的情况下实现此功能。此函数应采用id
和新的person对象,然后在运行时更新父组件内的状态。这是一个例子。
editPerson(id, editedPerson) {
const people = this.state.people;
const newFragment = { [id]: editedPerson };
// create a new list of people, with the updated person in
this.setState({
people: Object.assign([], people, newFragment)
});
},
render() {
// ...
{this.state.people.map((person, index) => {
const edit = this.editPerson.bind(this, index);
return (
<People data={person} edit={edit}></People>
);
})}
// ...
}
然后在您的人员组件中,每当您对此人进行更改时,只需将该人员通过回调传递回父状态。
但是,如果您通过应用程序可视化数据流,那么您现在已经创建了一个类似于此的循环。
App
^
|
v
Person
找出应用程序中的数据来源不再是微不足道的(在如此小的应用程序中它仍然非常简单,但显然它越大越难以辨别。
最初,Facebook开发人员使用单向数据流编写了React应用程序,他们发现它很好。然而,需要数据上升到树上,这导致了危机。我们的数据流如何是单向的并仍然返回到树的顶部?在第七天,他们创建了Flux(1),看到它非常好。
Flux允许您将更改描述为操作并将其从组件传递到存储(自包含状态框),这些存储了解如何根据操作操纵其状态。然后,商店会告诉所有关心它的组件已经发生了变化,此时组件可以获取要渲染的新数据。
您可以重新获得单向数据流,其架构看起来像这样。
App <---- [Stores]
| ^
v |
Person --> Dispatcher
您可能希望创建一个人工商店来跟踪您的人员列表,而不是将您的状态保留在<App />
组件中。
也许它看起来像这样。
// stores/people-store.js
const people = [];
export function getPeople() {
return people;
}
function editPerson(id, person) {
// ...
}
function addPerson(person) {
// ...
}
function removePerson(id) {
// ...
}
现在,我们可以导出这些函数并让我们的组件直接调用它们,但这很糟糕,因为这意味着我们的组件必须知道商店的设计,我们希望将它们视为愚蠢的可能的。
相反,我们的组件创建了简单,可序列化的操作,我们的商店可以理解。以下是一些例子:
// remove person with id 53
{ type: 'PEOPLE_REMOVE', payload: 53 }
// create a new person called John Foo
{ type: 'PEOPLE_ADD', payload: { name: 'John Foo' } }
// edit person 13
{
type: 'PEOPLE_EDIT',
payload: {
id: 13,
person: { name: 'Unlucky Bill' }
}
}
这些操作不必具有这些特定的密钥,它们甚至不必是对象,这只是来自Flux Standard Actions的惯例。
现在,我们告诉我们的商店,当他们到达时如何处理这些行为。
// stores/people-store.js
// ...
dispatcher.register(function(action) {
switch(action.type) {
case 'PEOPLE_REMOVE':
removePerson(action.payload);
case 'PEOPLE_ADD':
addPerson(action.payload);
case 'PEOPLE_EDIT':
editPerson(action.payload.id, action.payload.person);
}
});
呼。到目前为止,很多工作都在那里。
现在我们可以开始从我们的组件中发送这些操作。
// components/people.js
// ...
onEdit(editedPerson) {
dispatcher.dispatch({
type: 'PEOPLE_EDIT',
payload: {
id: this.props.id,
person: editedPerson
}
});
}
onRemove() {
dispatcher.dispatch({
type: 'PEOPLE_REMOVE',
payload: this.props.id
});
}
// ...
编辑此人时,请调用this.onEdit
方法,然后将相应的操作发送到您的商店。移除一个人同样如此。通常情况下,您会将此内容移动到动作创建者中,但这又是另一个主题。
好的,终于到了某个地方!现在,我们的组件可以创建更新商店中数据的操作。我们如何将这些数据反馈到我们的组件中?
最初,它非常简单。我们可以要求商店在我们的顶级组件中,只需要询问数据。
// components/app.js
import { getPeople } from './stores/people-store';
// ...
constructor() {
super();
this.state = { people: getPeople() };
}
我们可以用完全相同的方式传递这些数据,但是当数据发生变化时会发生什么?
Flux的官方立场基本上是&#34;不是我们的问题&#34;。 Their examples使用Node的Event Emitter类允许商店接受商店更新时调用的回调函数。
这允许您编写看起来像这样的代码:
componentWillMount() {
peopleStore.addListener(this.peopleUpdated);
},
componentWillUnmount() {
peopleStore.removeListener(this.peopleUpdated);
},
peopleUpdated() {
this.setState({ people: getPeople() });
}
真的,这个球在你的球场上。还有许多其他策略可以将数据恢复到您的程序中。 Reflux自动为您创建listen方法,Redux允许您以声明方式指定哪些组件接收商店的哪些部分作为props,然后它处理更新。在Flux上花足够的时间,你会找到一个偏好。
现在,您可能正在思考,blimey - 这似乎需要付出很多努力才能将编辑功能添加到组件中;你是对的,是的!
对于小型应用程序,您可能不需要Flux。
当然有很多好处,但额外的复杂性并不总是有道理的。随着您的应用程序的增长,您会发现如果您将其应用起来,那么管理,维护和调试将会更加容易。
诀窍在于知道什么时候使用Flux架构是合适的,并且希望在时机成熟时,这个过长而漫无边际的答案将为您解决问题。