为什么React组件不渲染?
我正在做简单的反应的应用程序。我需要下订单。对于该订单,我需要一份所有用户和产品的清单。我有CreateOrder
组件,其state
- users
和products
中有2个变量。这里是构造函数为什么React组件不渲染?
this.state.orders = OrderStore.getState()
this.state.products = ProductStore.getState()
this.state.users = UserStore.getState()
this.onOrderChange = this.onOrderChange.bind(this)
this.onProductChange = this.onProductChange.bind(this)
this.onUserChange = this.onUserChange.bind(this)
this.createOrder = this.createOrder.bind(this)
this.renderProducts = this.renderProducts.bind(this)
this.renderUsers = this.renderUsers.bind(this)
this.users = []
this.products = []
正如你可以看到我使用3商店,因此更新这些商店3功能。之后,我有2个功能来呈现产品和用户。这些功能将所有产品和用户从其各自的变量中提取出来,并创建一个option
标签阵列,用于<select>
。这些数组存储在this.users
和this.products
中,以便我可以使用它们。
这里是renderUsers
代码和renderProducts
renderUsers(){
this.users = this.state.users.users.map((user, i) => (
<option
key={i + 1}
value={user}>
{user.username}
</option>
)
)
console.log(this.users)
}
renderProducts(){
this.products = this.state.products.products.map((product, i) => (
<option
key={i + 1}
value={product}>
{product.name}
</option>
)
)
console.log(this.products)
}
我看不出太大两者之间的差别。阵列制成并填充option
标记后,我使用console.log
查看结果。这里是一张实际图片
正如你所看到的那样,有2个对象数组,每个对象都按照预期反应组件。
当我渲染CreateOrder
我使用this.products
和this.users
进行渲染,并且首先它们是空的。然后当商店中的东西发生变化时(它们充满了所有产品和用户)执行功能renderUsers
和renderProducts
,因此阵列中充满了数据。当发生这种情况时,我期望视图被更新。
现在的问题 - 当发生这种情况,并且this.products
正在更新时,视图实际上会发生变化,并且实际上会更新所有产品的列表。但是 - 使用相同的逻辑和相同的功能和代码 - 用户不会被更新。他们保持不变。即使我将它们作为数组中的元素,并且我在render
中调用此数组,但它们并未更新。
要在这里添加一些信息是我的整个CreateOrder
文件。
/**
* Created by PC on 01-Aug-17.
*/
import React from 'react'
import OrderActions from '../actions/OrderActions'
import ProductActions from '../actions/ProductActions'
import ProductStore from '../stores/ProductStore'
import UserActions from '../actions/UserActions'
import UserStore from '../stores/UserStore'
import OrderStore from '../stores/OrderStore'
import Select from './sub-components/SelectComponent'
import Input from './sub-components/InputComponent'
export default class CreateOrder extends React.Component {
constructor (props) {
super(props)
this.state = {}
this.state.orders = OrderStore.getState()
this.state.products = ProductStore.getState()
this.state.users = UserStore.getState()
this.onOrderChange = this.onOrderChange.bind(this)
this.onProductChange = this.onProductChange.bind(this)
this.onUserChange = this.onUserChange.bind(this)
this.createOrder = this.createOrder.bind(this)
this.renderProducts = this.renderProducts.bind(this)
this.renderUsers = this.renderUsers.bind(this)
this.users = []
this.products = []
}
componentDidMount() {
OrderStore.listen(this.onOrderChange)
ProductStore.listen(this.onProductChange)
UserStore.listen(this.onUserChange)
ProductActions.getAllProducts()
UserActions.getAllUsers()
}
componentWillUnmount() {
OrderStore.unlisten(this.onOrderChange)
UserStore.unlisten(this.onUserChange)
ProductStore.unlisten(this.onProductChange)
}
onOrderChange (state) {
this.setState({orders:state})
}
onProductChange(state) {
this.setState({products:state})
this.renderProducts()
}
onUserChange(state){
this.setState({users:state})
this.renderUsers()
}
createOrder (event) {
event.preventDefault()
let data = {}
data['username'] = this.state.orders.username
data['productName'] = this.state.orders.product.name
data['productPrice'] = this.state.orders.product.price
data['quantity'] = this.state.orders.quantity
OrderActions.addOrder(data)
}
renderUsers(){
this.users = this.state.users.users.map((user, i) => (
<option
key={i + 1}
value={user}>
{user.username}
</option>
)
)
console.log(this.users)
}
renderProducts(){
this.products = this.state.products.products.map((product, i) => (
<option
key={i + 1}
value={product}>
{product.name}
</option>
)
)
console.log(this.products)
}
render() {
return (
<div>
<div className='has-error'>
{this.state.orders.error}
</div>
<div className='has-success'>
{this.state.orders.success}
</div>
<form>
<select>{this.users}</select>
<select>{this.products}</select>
<div className="inputField">
<label htmlFor='title'>Quantity</label>
<Input
type='number'
name='price'
id='price'
placeholder='Price'
onChange={OrderActions.handleQuantityChange}
value={this.state.quantity}/>
</div>
<input type='submit' onClick={this.createOrder.bind(this)} value='Create Order'/>
</form>
</div>
)
}
}
我不确定问题的实际原因,但我认为这与setState的异步行为有关,我们无法期望在setState后面更新状态值。
检查此answer有关setState的异步行为的更多详细信息。
解决方案:
存储在状态变量或在全局变量中的UI组件是不是一个好主意,所有的UI逻辑应该内呈现的方法,因此而不是存储在全局变量的选项,直接返回从那个功能。
无论何时我们做setState
反应将重新渲染组件并用新数据更新ui。
第一步 -使用这些方法,去除另一个函数调用:
onProductChange(state) {
this.setState({products:state})
}
onUserChange(state){
this.setState({users:state})
}
第二步 - 使用这些方法来呈现的选项:
renderUsers(){
if(!this.state.users.users) return null;
return this.state.users.users.map((user, i) => (
<option
key={i + 1}
value={user}>
{user.username}
</option>
)
)
}
renderProducts(){
if(!this.state.products.products) return null;
return this.state.products.products.map((product, i) => (
<option
key={i + 1}
value={product}>
{product.name}
</option>
)
)
}
第三步 -调用这些函数从渲染到创建选项:
<select>{this.renderUsers()}</select>
<select>{this.renderProducts()}</select>
setState
是异步的。尝试将您的this.renderUsers();
置于componentWillUpdate
生命周期方法中。
当接收到新的道具或状态时,立即调用componentWillUpdate()。使用此作为更新发生之前执行准备的机会。此方法不用于初始渲染。
您还可以使用的setState(updater, [callback])的callback
说法。