我在React从事工作已有一年多,并且读过Thinking in react,Lifting state up和State and lifecycle。
我了解到,React的数据流概念是单向数据流。
这些页面的引用:
React的单向数据流(也称为单向绑定)使一切保持模块化和快速。
请记住:React只是关于组件层次结构中的单向数据流。可能尚不清楚哪个组件应拥有什么状态。对于新手来说,这通常是最具挑战性的部分,因此请按照以下步骤进行操作:...
如果您将一棵组件树想象成道具的瀑布,那么每个组件的状态就像是一个附加的水源,该水源在任意点处将其加入,但还会向下流动。
据我了解,以下示例是不允许的,因为我正在将子级状态数据传递给父级。但是我看到一些开发人员像这样工作:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { fromParent: null };
}
addSomething(stateValueFromChild) {
this.setState({fromParent: stateValueFromChild});
}
render() {
return <Child
addSomething={(stateValueFromChild) => this.addSomething(stateValueFromChild)}>
// ...
</Child>;
}
}
class Child extends React.Component {
constructor(props) {
super(props);
this.state = { fromChild: 'foo' };
}
render() {
return <Form onSubmit={() => this.props.addSomething(this.state.fromChild)}>
// ...
</Form>;
}
}
我的问题是:
答案 0 :(得分:1)
这是允许的,您的代码没有任何问题,但是我不会将其从子级传递到父级。您要做的只是调用在props中传递并由事件触发的带有一些参数的方法,在您的示例中,该方法是child的state值,但是它可以是任何其他变量。父组件对此参数的性质一无所知,它仅接收该值并能够执行任何操作,例如将其自身的状态更改为另一个状态。如果孩子的状态将改变,则在没有再次触发onSubmit
事件的情况下,父将不会收到此更新。但是,孩子总是会收到父母的更新,并在更改道具时自动将其重新呈现。当然,某些道具可能是某些父母的状态。这是行为上的主要区别。
有一篇很好的文章详细解释了这一点:Props down, Events Up
答案 1 :(得分:1)
这真的不允许吗? 为什么这不应该模块化且快速?
很好的问题。这是允许的。使其正常工作有点棘手,因为这里有状态同步。在现代的前端世界中,状态同步被认为是一项非常具有挑战性的任务。
当您需要在两个方向上同步状态时,将出现问题。例如,如果此子窗体用于编辑列表的某些元素,而您正在更改列表的当前元素。您将需要子组件来检测这种情况,并在特定的UI更新期间将其本地状态与props中的新元素同步。只要您没有,就可以了。
这真的阻止了单向数据流,变成了双向数据流吗?
不是,不是。它仍然是单向数据流,因为React在设计上无法以任何其他方式工作; UI更新始终从上到下进行。在您的示例中,您的孩子触发了一个事件,该事件导致父对象更新其状态(完全正常),这将导致父和的UI更新。如果您真的违反了“单向数据流”,您会感觉到。您将获得无限循环或类似的结果。
当我提起状态时,您将如何解决以下情况; 50个使用该子组件的具体父母,每个父母是否应该为他们正在使用的同一个孩子拥有相同的初始化子状态?
是的,这就是“解除国家”的含义。您将根状态组织为一棵反映子状态的树,然后将该状态的元素与回调一起传递给子状态以修改根状态。
答案 2 :(得分:0)
您的问题是绝对正确的,很多时候开发人员(包括我自己)都在努力将孩子的组件状态或道具传递给父组件。
我总是做逻辑来获取子组件中的下一个状态或下一个道具,并通过使用父组件的处理函数将下一个状态或下一个道具传递给父组件。
import React, { Component } from "react";
import { render } from "react-dom";
class Parent extends Component {
constructor(props) {
super(props);
this.handleSomething = this.handleSomething.bind(this); // binding method
this.state = {
fromParent: "foo"
};
}
handleSomething(value) {
this.setState(prevState => {
return {
fromParent: value
};
});
}
render() {
return (
<div>
<h1>State: {this.state.fromParent}</h1>
<Child handleSomething={this.handleSomething} />
</div>
);
}
}
class Child extends Component {
constructor(props) {
super(props);
this.state = {
fromChild: "bar"
};
}
render() {
return (
<div>
<button
onClick={e => {
const { fromChild } = this.state;
// do whatever as per your logic for get value from child pass to handleSomething function
// you can also do same for handling forms
this.props.handleSomething(fromChild);
}}
>
Click Me
</button>
</div>
);
}
}
render(<Parent />, document.getElementById("app"));