![](/img/trans.png)
[英]If the props for a child component are unchanged, does React still re-render it?
[英]React 16.13.1 - child component does not re-render when props change
我有一個父組件和一個子組件。 子組件最初將數據呈現到表單中,但在更改數據時,子組件不會更新。
父組件:
import React from 'react'
import { connect } from 'react-redux'
import styles from '../styles'
import ExpressionsForm from './expressionsForm'
class EditCondition extends React.Component {
constructor (props) {
super(props)
this.state = {
condition: null
}
this.updateExpression = this.updateExpression.bind(this)
this.changes = false
}
componentWillMount () {
let conditionid = this.props.data.id
let condition = this.props.conditions.find(c => {
return (c.id = conditionid)
})
this.setState({ condition })
}
updateExpression (e) {
let expressionid = e.currentTarget.dataset.expressionid
let field = e.currentTarget.dataset.field
let value = e.target.value
let condition = this.state.condition
let expression = condition.expressions[expressionid]
expression[field] = value
condition.expressions[expressionid] = expression
this.changes = true
this.setState({ condition })
console.log('updateExpression condition: ', condition)
}
render () {
let condition = this.state.condition
if (!this.state.condition) {
return (
<div>
The selected condition with ID "{this.props.data.id}" did not load. It
may not exist. Refresh and try again.
</div>
)
}
let groupOptions = this.props.gambitGroups.map(g => {
return (
<option value={g.id} key={'group' + g.id}>
{g.name}
</option>
)
})
console.log('RENDER editCondition: ', condition) // <-- Note: This always logs as expected
let expressionsJSX = condition.expressions.map((expression, i) => {
expression.id = i
console.log('expression: ', expression) // <-- Note: This always logs as expected
return (
<ExpressionsForm
key={'expressionsForm_' + i}
expression={expression}
deleteExpression={this.deleteExpression}
updateExpression={this.updateExpression}
updateExpressionData={this.updateExpressionData}
/>
)
})
return (
<table>
<thead>
<tr>
<th {...styles.modal.tableHeaderLeftAlign}>
Device & Data Point
</th>
<th {...styles.modal.tableHeaderLeftAlign}>Operator</th>
<th {...styles.modal.tableHeaderLeftAlign}>Value</th>
<th {...styles.modal.tableHeaderLeftAlign}>PlateValue</th>
<th {...styles.modal.tableHeaderLeftAlign}> </th>
</tr>
</thead>
<tbody>{expressionsJSX}</tbody>
</table>
)
}
}
export default connect(
(state, ownProps) => ({
user: state.user,
users: state.users,
gambitGroups: state.gambitGroups,
// deviceGroups: state.deviceGroups,
conditions: state.conditions,
reactions: state.reactions,
setEditMode: ownProps.setEditMode,
navByName: ownProps.navByName
}),
dispatch => ({
addImage: file => dispatch({ type: 'UPDATE_CONDITION_LOGO', file }),
updateCondition: condition =>
dispatch({ type: 'UPDATE_CONDITION', condition })
})
)(EditCondition)
和子組件:
import React from 'react'
import { connect } from 'react-redux'
import styles from '../styles'
class ExpressionsForm extends React.Component {
constructor (props) {
super(props)
this.state = {}
this.updateExpression = this.updateExpression.bind(this)
}
updateExpression (e) {
this.props.updateExpression(e)
}
render () {
let expression = this.props.expression
console.log('expression: ', expression) // Note: logs initial render only.
let data = expression.data
let deviceId = data.deviceId
let dataPointIndex = data.dataPointIndex
let operator = expression.operator
let plateValue = expression.plateValue
let value = expression.value
console.log('RENDER expressionForm: ', expression) // Note: logs initial render only
let deviceOptions = this.props.devices.map((device, i) => {
return (
<option value={device.id} key={'device_' + i}>
{device.userAssignedName}
</option>
)
})
let dataPointOptions = this.props.devices[0].inputs.map((input, i) => {
return (
<option value={input.id} key={'input_' + i}>
{input.name} currentValue: {input.value}
</option>
)
})
let operatorOptions = ['==', '!=', '<=', '>=', '<', '>'].map(
(operator, i) => {
return (
<option value={operator} key={'operator_' + i}>
{operator}
</option>
)
}
)
return (
<tr>
<td>
<select
{...styles.modal.inputSexy}
style={{ marginBottom: '20px' }}
data-field='deviceid'
data-expressionid={expression.id}
value={deviceId}
onChange={this.updateExpressionData}
>
<option value=''></option>
{deviceOptions}
</select>
<select
{...styles.modal.inputSexy}
data-field='dataPointIndex'
data-expressionid={expression.id}
value={dataPointIndex}
onChange={this.updateExpressionData}
>
<option value=''></option>
{dataPointOptions}
</select>
</td>
<td>
<select
{...styles.modal.inputSexy}
style={{ width: '75px' }}
data-field='operator'
data-expressionid={expression.id}
value={operator}
onChange={this.updateExpression}
>
<option value=''></option>
{operatorOptions}
</select>
</td>
<td>
<input
{...styles.modal.inputSexy}
style={{ width: '50px' }}
data-field='value'
data-expressionid={expression.id}
value={value}
onChange={this.updateExpression}
/>
</td>
<td>
<input
{...styles.modal.inputSexy}
style={{ width: '88px' }}
data-expressionid={expression.id}
data-field='plateValue'
value={plateValue}
onChange={this.updateExpression}
/>
</td>
<td>
<i className='fa fa-close'
data-expressionid={expression.id}
onClick={this.deleteExpression}
></i>
</td>
</tr>
)
}
}
export default connect(
(state, ownProps) => ({
user: state.user,
users: state.users,
devices: state.devices,
gambitGroups: state.gambitGroups,
// deviceGroups: state.deviceGroups,
conditions: state.conditions,
reactions: state.reactions,
setEditMode: ownProps.setEditMode,
navByName: ownProps.navByName
}),
dispatch => ({
addImage: file => dispatch({ type: 'UPDATE_XXX', file })
})
)(ExpressionsForm)
我在 redux 商店中有一組對象,稱為條件。 父組件獲取這些條件之一的 ID,找到正確的條件,並通過 componentWillMount 將其加載到 state 以供用戶修改。 條件上有一個對象數組,稱為表達式。 這些表達式中的每一個都傳遞給名為 ExpressionsForm 的子組件。
所以我們通過 map function 循環表達式並將生成的 JSX 作為表達式JSX 返回。
let expressionsJSX = condition.expressions.map((expression, i) => {
expression.id = i
console.log('expression: ', expression) // <-- Note: This always logs as expected
return (
<ExpressionsForm
key={'expressionsForm_' + i}
expression={expression}
deleteExpression={this.deleteExpression}
updateExpression={this.updateExpression}
updateExpressionData={this.updateExpressionData}
/>
)
})
請注意,將表達式傳遞給它 expression={expression}
在子組件的渲染中你會看到
let expression = this.props.expression
console.log('expression: ', expression) // Note: logs initial render only.
由於這是一個道具,因此無論是控制台記錄還是渲染到某個 JSX 中都無關緊要 - 當道具更改時,更改也應該重新渲染。 但在這種情況下它沒有這樣做。 為什么?
例如,我在 1 個條件下保存了 1 個表達式。 它呈現時,我單擊表達式的 plateValue 輸入字段 - 默認情況下包含 5 - 並嘗試在 5 之后添加 6。當父組件更新 state 時重新呈現,我在 console.log 中看到表達式的 plateValue 字段現在包含一個“56”……它只是不在子組件中呈現……??
這是一個示例 console.log
初始渲染:
渲染editCondition:{id:“1”,組:1,名稱:“溫度> = 75F”,元:“如果溫室> = 75F,打開AC直到5度低於75F”,表達式:Array(1)} editCondition.jsx:191 表達式:{數據:{…},運算符:>=",值:"75",plateValue:"5",id:0} 表達式Form.jsx:39 RENDER 表達式:{數據:{… },運算符:>=,值:“75”,plateValue:“5”,id:0}
單擊 plateValue 字段並添加“6”,父級重新渲染...並且:
editCondition.jsx:188 RENDER editCondition: {id: "1", group: 1, name: "Temperature >= 75F", meta: "If >= 75F in溫室打開AC直到5度低於75F",表達式: Array(1)} editCondition.jsx:191 表達式:{data: {…}, operator: ">=", value: "75", plateValue: "56", id: 0} editCondition.jsx:153 STATE SET: updateExpression 條件:{id,“1”:組,1:名稱,“溫度 >= 75F”:元,“如果溫室中 >= 75F,則打開空調直到比 75F 低 5 度”:表達式:Array(1)}
我在那里看到一個'plateValue:“56”'。 那么為什么不在子組件中重新渲染呢? 如此迷茫。
我已經嘗試過 componentWillReceiveProps、componentWillUpdate 等。 我什至無法讓這些觸發console.log。
發生了一些我無法弄清楚的事情。 我已經做 React 很長時間了,我很困惑。 這不再經常發生了。
在此先感謝您的幫助
PS 我確實看過 getDerivedStateFromProps - 文檔提供了示例很好,但他們沒有解釋 props 和 state 參數實際上是什么。 文檔很爛。 他們的解釋很爛。 他們的例子並沒有說明它實際上做了什么。 我只使用 componentWillReceiveProps 來知道道具何時發生變化,然后更新 state 或其他什么。 getDerivedStateFromProps 只是讓我感到困惑。 盡管如此,我還是玩弄了它,也無法讓它工作。
看起來同一個expression
object一直在傳遞。
React 在決定渲染時會檢查組件接收到的props
是否有變化。 發現所有的props
項都沒有改變,都是和之前一樣的對象,得出子組件不需要重新渲染的結論。 它不會對每個道具的所有屬性進行深入檢查。
這也解釋了為什么可以通過復制表達式 object 來強制重新渲染。 副本始終是新的 object,因此會導致重新渲染,無論其內容是否已更改。
您可以通過制作副本或將expression
object 分解為其屬性,然后將其中的每一個作為單獨的props
提供給孩子來避免這種情況。
最后一點,也可以通過將其作為expression={{...expression}}
傳遞來制作副本。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.