【发布时间】:2020-07-31 06:56:37
【问题描述】:
我有一个 React 组件 <ProductPrices>:
render() {
if (this.props.currentProduct.id !== 'new' && !this.props.currentProductPrices) {
this.props.fetchProductPrices(this.props.currentProduct.id);
}
return (
<div>
<div style={{backgroundColor: 'gray', paddingLeft: '10px', paddingTop: '10px', paddingBottom: '5px'}}>
<h1>Precios</h1>
</div>
<div>
{this.props.currentProductPrices && this.props.currentProductPrices.map((price, index) => {
console.log({detail: price});
return (
<ProductPricesEntry key={price.id} price={price} index={index} />
)
})}
</div>
</div>
)
}
如您所见,<ProductPrices> 包含许多子组件 <ProductPricesEntry>,其数量是动态的,取决于 Redux 状态变量 currentProductPrices:
render() {
console.log({entry: this.props.price});
return (
<div style={{display: 'flex', background: 'white', backgroundColor: 'lightgray', marginTop: '2px', paddingLeft: '10px', paddingTop: '10px', paddingBottom: '5px'}}>
<div className='col-10'>
<div fullWidth>
<h3>{this.props.price.prices_table.name}</h3>
</div>
<div fullWidth style={{display: 'flex'}}>
{this.props.price.current_price?
<div style={{textAlign: 'right'}} className='col-4'><strong>{currencyFormatter.format(this.props.price.current_price)}</strong></div>:
<div style={{textAlign: 'right', color: 'orange'}} className='col-4'>Ninguno</div>
}
{this.props.price.due_date?
<div style={{textAlign: 'center'}} className='col-4'><strong>{this.props.price.due_date}</strong></div>:
<div style={{textAlign: 'center', color: 'orange'}} className='col-4'>Ninguno</div>
}
{this.props.price.next_price?
<div style={{textAlign: 'right'}} className='col-4'><strong>{currencyFormatter.format(this.props.price.next_price)}</strong></div>:
<div style={{textAlign: 'right', color: 'orange'}} className='col-4'>Ninguno</div>
}
</div>
<div fullWidth style={{display: 'flex'}}>
<div style={{textAlign: 'right'}} className='col-4'>Actual</div>
<div style={{textAlign: 'center'}} className='col-4'>Vigencia</div>
<div style={{textAlign: 'right'}} className='col-4'>Próximo</div>
</div>
</div>
<div className='col-1'>
<IconButton color="primary" aria-label={''.concat('update-price-', this.props.price.id)}>
<i className="zmdi zmdi-edit zmdi-hc-fw" onClick={this.handleUpdateClick} />
</IconButton>
<Dialog fullWidth open={this.state.updateDialogOpen} arialabelledby={''.concat('update-price-', this.props.price.id)}>
<DialogTitle id={"".concat("update-price-", this.props.price.id)}>Actualizar Precio</DialogTitle>
<DialogContentText>
<div style={{paddingLeft: '25px'}}><h2>{this.props.currentProductData.name}</h2></div>
<div style={{paddingLeft: '25px'}}><h2>{this.props.price.prices_table.name}</h2></div>
</DialogContentText>
<DialogContent>
<FormControl fullWidth>
<InputLabel htmlFor="newPrice">Precio</InputLabel>
<Input
type="number"
id="newPrice"
name="newPrice"
value={this.state.newPrice}
onChange={this.handleChange}
startAdornment={<InputAdornment position="start">$</InputAdornment>}
/>
</FormControl>
<div fullWidth><TextField fullWidth label="Fecha" type="date" name="newDate" value={this.state.newDate} onChange={this.handleChange} /></div>
</DialogContent>
<DialogActions>
<Button onClick={this.handleDialogAcceptClick} name="accept" color="primary">
Aceptar
</Button>
<Button onClick={this.handleDialogCancelClick} name="cancel" color="secondary">
Cancelar
</Button>
</DialogActions>
</Dialog>
</div>
</div>
)
}
}
我在从<ProductPrices> 调用<ProductPricesEntry> 之前放置了console.log() 语句,并在渲染时放置在<ProductPricesEntry> 内部,我可以看到两个console.log() 语句都是第一次到达,但@ 内部的那个如果this.props.currentProductPrices 更改,则无法到达 987654338@:
这是this.props.currentPrices的值不一样,我可以在Redux Tools上看到变化:
问题在于,<ProductPricesEntry> 中的 console.log() 语句永远不会到达,这意味着它不会重新渲染,尽管更改的 Redux 状态值作为道具发送到组件并显示在内部。
我想我做错了什么,但我找不到。
编辑 这是reducer,它改变了必须导致重新渲染的状态:
case UPDATE_PRODUCT_PRICE_SUCCESS: {
if (state.currentPricesTable) {
let currentPricesTableProducts = [...state.currentPricesTableProducts];
let updatedProductIndex = currentPricesTableProducts.findIndex(product => product.id === action.payload.productPrice.id)
currentPricesTableProducts[updatedProductIndex]['next_price'] = action.payload.productPrice.next_price;
currentPricesTableProducts[updatedProductIndex]['due_date'] = action.payload.productPrice.start_date;
return {
...state,
currentPricesTableProducts: [...currentPricesTableProducts],
alert: {type: ALERT_SUCCESS, message: "El precio se actualizó existosamente."},
showMessage: true,
}
} else if (state.currentProduct) {
let currentProductPrices = [...state.currentProductPrices];
let updatedProductPriceIndex = currentProductPrices.findIndex(productPrice => productPrice.prices_table_product === action.payload.productPrice.prices_table_product)
currentProductPrices[updatedProductPriceIndex].next_price = action.payload.productPrice.next_price;
currentProductPrices[updatedProductPriceIndex].due_date = action.payload.productPrice.start_date;
return {
...state,
currentProductPrices: [...currentProductPrices],
alert: {type: ALERT_SUCCESS, message: "El precio se actualizó existosamente."},
showMessage: true,
}
} else {
return {
...state
}
}
}
如您所见,我将状态变量 currentProductPrices 替换为一个全新的数组。
我在从reducer返回之前添加了一个console.log(),我可以看到数据是正确的。我可以看到变化:
【问题讨论】:
-
currentProductPrices 必须是重新渲染的新数组。如果内部发生了某些变化,则不会导致重新渲染。当您使用地图创建 ProductPricesEntry 元素时,也不要忘记将关键属性添加到它们。
-
@EugeneMankovski 我做了一个版本,请检查一下。
-
尝试在第二次返回之前将console.log() 放入reducer 中,然后返回更新后的currentProductPrices。还将 key 属性添加到
-
另外 this.props.fetchProductPrices(this.props.currentProduct.id) 应该在新的 js 周期被调用以不影响渲染。您可以使用 setTimeout 函数或 useEffect 挂钩。
-
@EugeneMankovski 我都做了,但什么也没做。
标签: reactjs