即使在调用 setState 之后,React.js 子状态也不会重新渲染?
Posted
技术标签:
【中文标题】即使在调用 setState 之后,React.js 子状态也不会重新渲染?【英文标题】:React.js child state not re-rendering even after calling setState? 【发布时间】:2019-08-09 03:12:23 【问题描述】:我有一个包含一个子组件的应用程序,我想在 setState 更新父状态下的 bookInput 时重新渲染它。我正在使用 axios 从谷歌的 book api 请求信息。出于某种原因,即使状态正在更新,孩子也不会重新渲染。如果可以的话请帮忙!谢谢!
import React, Component from 'react';
import axios from 'axios';
class App extends Component
constructor(props)
super(props);
this.state =
bookInput: 'ender',
bookSubmitted: 'initial'
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.handleSubmitEmpty = this.handleSubmitEmpty.bind(this);
handleChange(e)
this.setState(bookInput: e.target.value);
console.log(this.state.bookInput);
//this.setState(bookSubmitted: false);
handleSubmit(e)
e.preventDefault();
//this.setState(bookSubmitted: true)
const name = this.state.bookInput;
this.setState(bookInput: name);
console.log(this.state);
this.setState(bookSubmitted: 'userSub');
handleSubmitEmpty(e)
alert('please enter an item to search for');
e.preventDefault();
render()
return (
<div className="App">
<header className = "App-header">
<h1>Book Search App</h1>
</header>
<form className = "form-style" onSubmit = this.state.bookInput ? this.handleSubmit: this.handleSubmitEmpty>
<label>
<input type="text" className = "input-style"
value = this.state.bookInput onChange = this.handleChange>
</input>
</label>
<button type="submit">search books</button>
</form>
/* <Book bookInput = this.state.bookInput/> */
/*this.state.bookSubmitted && <Book bookInput = this.state.bookInput/>*/
(this.state.bookSubmitted === 'initial' || this.state.bookSubmitted === 'userSub') &&
<Book bookInput = this.state.bookInput/>
</div>
);
export default App;
class Book extends Component
constructor(props)
super(props);
this.state =
//bookInput2: "ender",
bookTitles: [],
bookExample: '',
isLoading: false
this.bookClick = this.bookClick.bind(this);
bookClick(book)
console.log(book);
console.log(book.volumeInfo.infoLink);
const bookURL = book.volumeInfo.infoLink;
window.open(bookURL);
componentDidMount()
//this.setState( isLoading: true );
this.setState(isLoading: true);
axios.get(`https://www.googleapis.com/books/v1/volumes?q=$this.props.bookInput`)
.then((response) =>
const bookExample1 = response.data.items;
console.log(bookExample1);
this.setState(bookTitles: bookExample1, isLoading: false);
)
.catch((error) =>
console.error('ERROR!', error);
this.setState(isLoading: false);
);
render()
return (
<div>
this.state.bookTitles ? (
<div>
<h2>book list</h2>
<ul className = 'list-style'>
this.state.isLoading &&
(<div>
loading book list
</div>)
this.state.bookTitles.map(book => (
<li key=book.id>
<span className = 'book-details book-title' onClick = () => this.bookClick(book)> book.volumeInfo.title</span>
<br/>
book.volumeInfo.imageLinks &&
<img src = book.volumeInfo.imageLinks.thumbnail/>
book.volumeInfo.description &&
<span className = 'book-details'>book.volumeInfo.description</span>
<br/>
<span className = 'book-details'>Categories book.volumeInfo.categories</span>
</li>
))
</ul>
</div>) :
(<p>sorry, that search did not return anything</p>)
</div>
);
【问题讨论】:
请删减你的代码,只保留相关的部分。 【参考方案1】:您可能正在寻找类似的东西吗? https://stackblitz.com/edit/react-snoqkt?file=index.js
上面的代码可以进一步简化和组织,但它给了你一些想法。
代码的主要变化。
将 Api 调用从 componentDidMount 生命周期事件更改为名为 getInitialdata
的新方法,该方法在 handleSubmit 中调用。
getInitialdata(name)
axios.get(`https://www.googleapis.com/books/v1/volumes?q=$name`)
.then((response) =>
const bookExample1 = response.data.items;
console.log(bookExample1);
this.setState(bookTitles: bookExample1, isLoading: false, bookSubmitted: 'userSub');
)
.catch((error) =>
console.error('ERROR!', error);
this.setState(isLoading: false, bookSubmitted: 'userSub');
);
改变了 Child 组件的使用方式。
<Book bookTitles=this.state.bookTitles isLoading=this.state.isLoading/>
您的代码的问题是您正在组件的 didMount 方法中进行 API 调用。只有在安装组件时才会调用此生命周期事件。不是更新的时候。 当您在文本框中输入一些输入并单击“搜索书籍”时,componentDidMount 事件不会触发。这就是 API 调用从第二次开始没有发生的原因。
https://reactjs.org/docs/react-component.html#componentdidmount 上有关生命周期事件的更多信息
【讨论】:
好的,我更新了它,现在可以使用了!我只是将加载保留在父组件中-您认为这重要吗?在这里:codesandbox.io/s/wyjovm13l7 您可以在父组件中添加加载,因为您在那里进行 api 调用。据我所知,这不重要。我的一个建议是,可以创建一个新的加载组件,以便在需要时可以重复使用。【参考方案2】:我已获取您的代码并将其推断为this sandbox。正如您所说,您的父组件状态正在更新,但问题是子组件不会更改 its 状态。
状态更改总是会触发 React 中的重新渲染。唯一的问题是,您的子组件正在管理它自己的状态,而这并没有直接改变。相反,它只是一次又一次地接收新的道具,但没有对它们做任何事情。
如果您查看 <Book />
组件的代码,您只会修改其在 componentDidMount
上的状态,这只会发生一次。如果您想以编程方式使其更新,您可以执行以下两项操作之一。
-
从子组件中移除状态,使其完全依赖于 props,使其与父组件保持同步
使用
componentDidUpdate
生命周期方法 (docs) 选择何时更改子节点的状态(这将触发重新渲染)
【讨论】:
感谢您向我展示沙盒,非常方便!我让它像这样工作:codesandbox.io/s/wyjovm13l7 我想通过你的第一个建议,因为我不确定如何正确使用 componentDidUpdate。以上是关于即使在调用 setState 之后,React.js 子状态也不会重新渲染?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 setState 之后调用 getDerivedStateFromProps?