让我们假设我们有一个父组件:
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
titleState: undefined
};
}
componentWillMount() {
// import Title string from JSON in order to assign it to titleState
this.setState({ titleState: jsonResponse["title"] });
}
render() {
return (
<div>
<ChildComponent title={this.state.titleState} />
</div>
);
}
}
class ChildComponent extends React.Component {
render() {
return (
<div>
<h>Title is: {this.props.title}</h>
</div>
);
}
}
让我们假设带有标题的服务器响应会明显延迟,因此一开始我们在屏幕上看到的是:
Title is:
但是当响应带有jsonResponse [“ title”]值时:“ Bone Collector” 我们看到:
Title is: Bone Collector
现在的问题是:
是否可以确保父组件中的状态更改将导致子组件重新呈现,以便通过道具以新的父组件状态进行更新?
答案 0 :(得分:3)
反应应用diffing algorithm
来查看两个trees
之间的差异。
组件更新时,实例保持不变,因此在渲染之间保持状态。 React更新基础组件实例的属性以匹配新元素,并在基础实例上调用componentWillReceiveProps()
和componentWillUpdate()
。
在您的情况下,由于更改了传递给prop
的{{1}},因此将用childComponent
重新呈现子组件。但是,react将new prop value
应用于子组件的diffing algorithm
和old rendered output
,并且只会更新已更改的内容。
为满足您的好奇心,请阅读:https://reactjs.org/docs/reconciliation.html
答案 1 :(得分:1)
专门讨论帖子中的代码片段。
是。可以保证父组件的状态更改将导致子组件重新呈现。
现在的问题是
为什么子组件将使用新值进行更新?
因为它是以某种方式绑定的,所以无论何时父项更改中的值,它都会将new tree
与old tree
进行比较(The Diffing Algorithm)并标识出更改的内容和仅修补了更改的部分在树上。
<h>Title is: {this.props.title}</h>
何时不更新子组件?
只要您从父母那里获得props
并将其存储在孩子的state
中,并使用state
值作为数据源,例如:
state ={
title: this.props.title
}
<h>Title is: {this.state.title}</h>
现在,在这种情况下,数据源为state
。当子组件mounts
时仅设置一次。每当父组件中的state
发生更改时,您的子组件都不知道任何props
的更改,结果您的子组件将不会使用新值进行更新。
要使此方案可行,将需要一种称为componentDidUpdate
的生命周期方法,在该方法中,我们可以将新的props
设置为state
,然后随着状态的改变,您的子组件将重新渲染。
componentDidUpdate(prevProps){
if(prevProps.title !== this.props.title){
this.setState({title: this.props.title})
}
}
答案 2 :(得分:1)
如果您在setState()
的{{1}}中调用componentWillMount()
,它将不会重新渲染 Parent
,但会在Child
组件的初始渲染,并忽略您最初在父构造函数中设置的值。
根据React文档:
UNSAFE_componentWillMount()在挂载发生之前被调用。它 在render()之前调用,因此同步调用setState() 此方法不会触发额外的渲染。通常,我们 建议使用constructor()代替初始化状态。
但是,如果您异步调用Child
,则setState()
的状态将被更新,并且通过您的代码,发送到Parent
组件的道具将被更改,从而触发重新渲染Child
组件。
但是,不建议从Child
进行API调用,而是从UNSAFE_componentWillMount
组件的componentDidMount()
进行API调用。
这个简单的示例将帮助您理解这一点:
Parent
class Parent extends React.Component{
constructor(props){
super(props);
this.state = {foo: "first"};
}
UNSAFE_componentWillMount(){
this.setState({foo: "second"}); //ignore the initial state value
setTimeout(() => this.setState({foo: "third"}), 5000);
}
render(){
return <Child foo={this.state.foo}></Child>
}
}
class Child extends React.Component{
constructor(props){
super(props);
}
render(){
console.log("Child rendered", this.props.foo);
return <div>This is the Child</div>
}
}
ReactDOM.render(<Parent/>, document.getElementById("root"));
在以上代码段中,未在控制台中看到<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
,因为在Child rendered first
中调用了setState()
,从而覆盖了{{1 }}从componentWillMount()
到foo
。
这是因为state
在挂载发生之前立即被调用,因此没有重新渲染的机会,但是初始渲染将使用first
中设置的second
的数据