我有一个父组件和一个子组件。子组件最初将数据呈现为表单,但在更改数据后,子组件不会更新。
父组件:
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 存储中有一个名为 Condition 的对象数组。父组件获取这些条件之一的 ID,找到正确的条件,并通过 componentWillMount 将其加载到状态以供用户修改。条件有一个对象数组,称为表达式。每个表达式都传递给名为 ExpressionsForm 的子组件。
所以我们通过 map 函数遍历表达式并将结果 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.
因为这是一个 prop,它是否被 console.log'd 或渲染到一些 JSX 并不重要 - 当 prop 更改时,更改也应该重新渲染。但在这种情况下它没有这样做。为什么?
例如,我在 1 个条件下保存了 1 个表达式。它呈现,我单击表达式的plateValue 输入字段 - 默认情况下包含 5 - 并尝试在 5 之后添加 6。当父组件更新状态时重新呈现,我在 console.log 中看到表达式的 plateValue 字段现在包含一个“56”...它只是不在子组件中呈现....!?
这是一个示例 console.log
<块引用>初始渲染:
RENDER editCondition: {id: "1", group: 1, name: "Temperature >= 75F", 元:“如果在温室中 >= 75F 打开空调,直到比温度低 5 度 75F", 表达式: Array(1)} editCondition.jsx:191 表达式: {data: {…}, 运算符: ">=", 值: "75", plateValue: "5", id: 0} expressionForm.jsx:39 RENDER expressionForm: {data: {…}, operator: ">=", value: "75",plateValue: "5", id: 0}
点击 plateValue 字段并添加一个“6”,父级重新渲染...并且:
editCondition.jsx:188 RENDER editCondition: {id: "1", group: 1, name: “温度 >= 75F”,元:“如果温室中的温度 >= 75F 打开空调,直到 比 75F 低 5 度”,表达式:Array(1)} editCondition.jsx:191 表达式:{data: {…}, operator: ">=", value: "75", plateValue: "56", id: 0} editCondition.jsx:153 状态设置! 更新表达式条件:{id: "1", group: 1, name: "Temperature >= 75F”,元:“如果温室中的温度 >= 75F 打开空调,直到温度降低 5 度 比 75F",表达式:Array(1)}
我在那里看到一个 'plateValue: "56"'。那么为什么不重新渲染 子组件?好纠结。
我尝试过 componentWillReceiveProps、componentWillUpdate 等。我什至无法让它们触发 console.log。
发生了一些我无法弄清楚的事情。我做 React 已经很长时间了,但我很困惑。这种情况不再经常发生。
预先感谢您的帮助
PS 我确实看过 getDerivedStateFromProps - 文档提供了示例很棒,但它们没有解释 props 和 state 参数实际上是什么。文档很烂。他们的解释很烂。他们的例子并没有说明它的实际作用。我只使用 componentWillReceiveProps 来知道道具何时发生变化,然后更新状态或其他什么。 getDerivedStateFromProps 只是让我感到困惑。尽管如此,我还是玩弄了它,但也无法让它工作。
答案 0 :(得分:1)
看起来一直在传入相同的 expression
object。
React 在决定渲染时检查组件收到的 props
是否有变化。它发现 props
项都没有改变,它们都是与之前相同的对象,并得出子组件不需要重新渲染的结论。它不会对每个道具的所有属性进行深入检查。
这也解释了为什么可以通过复制表达式对象来强制重新渲染。副本始终是一个新对象,因此会导致重新渲染,无论其内容是否已更改。
您可以像以前一样避免这种情况,方法是制作一个副本,或者将 expression
对象分解为它的属性,然后将每个属性作为单独的 props
提供给子对象。
最后一点,也可以通过将其作为 expression={{...expression}}
传入来制作副本。