Joh*_*ohn 2 javascript reactjs
我有一个父组件和一个子组件。子组件最初将数据呈现到表单中,但在更改数据后,子组件不会更新。
\n父组件:
\nimport React from \'react\'\nimport { connect } from \'react-redux\'\nimport styles from \'../styles\'\nimport ExpressionsForm from \'./expressionsForm\'\n\nclass EditCondition extends React.Component {\n constructor (props) {\n super(props)\n this.state = {\n condition: null\n }\n\n this.updateExpression = this.updateExpression.bind(this)\n\n this.changes = false\n }\n\n componentWillMount () {\n let conditionid = this.props.data.id\n let condition = this.props.conditions.find(c => {\n return (c.id = conditionid)\n })\n this.setState({ condition })\n }\n\n updateExpression (e) {\n let expressionid = e.currentTarget.dataset.expressionid\n let field = e.currentTarget.dataset.field\n let value = e.target.value\n let condition = this.state.condition\n let expression = condition.expressions[expressionid]\n expression[field] = value\n condition.expressions[expressionid] = expression\n this.changes = true\n this.setState({ condition })\n console.log(\'updateExpression condition: \', condition)\n }\n\n render () {\n let condition = this.state.condition\n if (!this.state.condition) {\n return (\n <div>\n The selected condition with ID "{this.props.data.id}" did not load. It\n may not exist. Refresh and try again.\n </div>\n )\n }\n\n let groupOptions = this.props.gambitGroups.map(g => {\n return (\n <option value={g.id} key={\'group\' + g.id}>\n {g.name}\n </option>\n )\n })\n\n console.log(\'RENDER editCondition: \', condition) // <-- Note: This always logs as expected\n\n let expressionsJSX = condition.expressions.map((expression, i) => {\n expression.id = i\n console.log(\'expression: \', expression) // <-- Note: This always logs as expected\n return (\n <ExpressionsForm\n key={\'expressionsForm_\' + i}\n expression={expression}\n deleteExpression={this.deleteExpression}\n updateExpression={this.updateExpression}\n updateExpressionData={this.updateExpressionData}\n />\n )\n })\n\n return (\n <table>\n <thead>\n <tr>\n <th {...styles.modal.tableHeaderLeftAlign}>\n Device & Data Point\n </th>\n <th {...styles.modal.tableHeaderLeftAlign}>Operator</th>\n <th {...styles.modal.tableHeaderLeftAlign}>Value</th>\n <th {...styles.modal.tableHeaderLeftAlign}>PlateValue</th>\n <th {...styles.modal.tableHeaderLeftAlign}> </th>\n </tr>\n </thead>\n <tbody>{expressionsJSX}</tbody>\n </table>\n \n )\n }\n}\n\nexport default connect(\n (state, ownProps) => ({\n user: state.user,\n users: state.users,\n gambitGroups: state.gambitGroups,\n // deviceGroups: state.deviceGroups,\n conditions: state.conditions,\n reactions: state.reactions,\n setEditMode: ownProps.setEditMode,\n navByName: ownProps.navByName\n }),\n dispatch => ({\n addImage: file => dispatch({ type: \'UPDATE_CONDITION_LOGO\', file }),\n updateCondition: condition =>\n dispatch({ type: \'UPDATE_CONDITION\', condition })\n })\n)(EditCondition)\nRun Code Online (Sandbox Code Playgroud)\n和子组件:
\nimport React from \'react\'\nimport { connect } from \'react-redux\'\nimport styles from \'../styles\'\n\nclass ExpressionsForm extends React.Component {\n constructor (props) {\n super(props)\n this.state = {}\n\n this.updateExpression = this.updateExpression.bind(this)\n }\n\n updateExpression (e) {\n this.props.updateExpression(e)\n }\n\n render () {\n let expression = this.props.expression\n console.log(\'expression: \', expression) // Note: logs initial render only.\n let data = expression.data\n let deviceId = data.deviceId\n let dataPointIndex = data.dataPointIndex\n let operator = expression.operator\n let plateValue = expression.plateValue\n let value = expression.value\n\n console.log(\'RENDER expressionForm: \', expression) // Note: logs initial render only\n\n let deviceOptions = this.props.devices.map((device, i) => {\n return (\n <option value={device.id} key={\'device_\' + i}>\n {device.userAssignedName}\n </option>\n )\n })\n\n let dataPointOptions = this.props.devices[0].inputs.map((input, i) => {\n return (\n <option value={input.id} key={\'input_\' + i}>\n {input.name} currentValue: {input.value}\n </option>\n )\n })\n\n let operatorOptions = [\'==\', \'!=\', \'<=\', \'>=\', \'<\', \'>\'].map(\n (operator, i) => {\n return (\n <option value={operator} key={\'operator_\' + i}>\n {operator}\n </option>\n )\n }\n )\n\n return (\n <tr>\n <td>\n <select\n {...styles.modal.inputSexy}\n style={{ marginBottom: \'20px\' }}\n data-field=\'deviceid\'\n data-expressionid={expression.id}\n value={deviceId}\n onChange={this.updateExpressionData}\n >\n <option value=\'\'></option>\n {deviceOptions}\n </select>\n <select\n {...styles.modal.inputSexy}\n data-field=\'dataPointIndex\'\n data-expressionid={expression.id}\n value={dataPointIndex}\n onChange={this.updateExpressionData}\n >\n <option value=\'\'></option>\n {dataPointOptions}\n </select>\n </td>\n <td>\n <select\n {...styles.modal.inputSexy}\n style={{ width: \'75px\' }}\n data-field=\'operator\'\n data-expressionid={expression.id}\n value={operator}\n onChange={this.updateExpression}\n >\n <option value=\'\'></option>\n {operatorOptions}\n </select>\n </td>\n <td>\n <input\n {...styles.modal.inputSexy}\n style={{ width: \'50px\' }}\n data-field=\'value\'\n data-expressionid={expression.id}\n value={value}\n onChange={this.updateExpression}\n />\n </td>\n <td>\n <input\n {...styles.modal.inputSexy}\n style={{ width: \'88px\' }}\n data-expressionid={expression.id}\n data-field=\'plateValue\'\n value={plateValue}\n onChange={this.updateExpression}\n />\n </td>\n <td>\n <i className=\'fa fa-close\'\n data-expressionid={expression.id}\n onClick={this.deleteExpression}\n ></i>\n \n </td>\n </tr>\n )\n }\n}\n\nexport default connect(\n (state, ownProps) => ({\n user: state.user,\n users: state.users,\n devices: state.devices,\n gambitGroups: state.gambitGroups,\n // deviceGroups: state.deviceGroups,\n conditions: state.conditions,\n reactions: state.reactions,\n setEditMode: ownProps.setEditMode,\n navByName: ownProps.navByName\n }),\n dispatch => ({\n addImage: file => dispatch({ type: \'UPDATE_XXX\', file })\n })\n)(ExpressionsForm)\nRun Code Online (Sandbox Code Playgroud)\n我在 redux 存储中有一个名为 Conditions 的对象数组。父组件获取这些条件之一的 ID,找到正确的条件,并通过 componentWillMount 将其加载到状态中以供用户修改。该条件有一个称为表达式的对象数组。这些表达式中的每一个都被传递到名为 ExpressionsForm 的子组件。
\n因此,我们通过 map 函数循环表达式,并将生成的 JSX 作为表达式JSX 返回。
\nlet expressionsJSX = condition.expressions.map((expression, i) => {\n expression.id = i\n console.log(\'expression: \', expression) // <-- Note: This always logs as expected\n return (\n <ExpressionsForm\n key={\'expressionsForm_\' + i}\n expression={expression}\n deleteExpression={this.deleteExpression}\n updateExpression={this.updateExpression}\n updateExpressionData={this.updateExpressionData}\n />\n )\n })\nRun Code Online (Sandbox Code Playgroud)\n请注意,已将表达式传递给它 expression={expression}
\n在子组件的渲染中你会看到
\n let expression = this.props.expression\n console.log(\'expression: \', expression) // Note: logs initial render only.\nRun Code Online (Sandbox Code Playgroud)\n由于这是一个 prop,无论它是被 console.log 还是渲染到某些 JSX 中都无关紧要 - 当 prop 更改时,更改也应该重新渲染。但在这种情况下它没有这样做。为什么?
\n例如,我在 1 个条件下保存了 1 个表达式。它渲染后,我单击表达式的 PlateValue 输入字段(默认情况下包含 5),并尝试在 5 之后添加 6。当父组件更新状态重新渲染时,我在 console.log 中看到表达式的 PlateValue 字段现在包含“56”...它只是不会在子组件中呈现...!?
\n这是一个 console.log 示例
\n\n\n初始渲染:
\nRENDER editCondition: {id: "1", group: 1, name: "Temperature >= 75F",\nmeta: "如果温室中 >= 75F 打开空调直到比 \n75F 低 5 度", 表达式: Array(1 )} editCondition.jsx:191 表达式:{数据:\n{\xe2\x80\xa6},运算符:">=,值:“75”,plateValue:“5”,id:0}\nexpressionsForm.jsx :39 RENDER 表达式形式:{数据:{\xe2\x80\xa6},运算符:\n">=,值:“75”,plateValue:“5”,id:0}
\n单击进入 PlateValue 字段并添加“6”,父级重新渲染...并且:
\neditCondition.jsx:188 RENDER editCondition: {id: "1", group: 1, name:\n"Temperature >= 75F", meta: "如果温室中 >= 75F 打开空调直到\n比 75F 低 5 度" ,表达式:Array(1)}\neditCondition.jsx:191 表达式:{数据:{\xe2\x80\xa6},运算符:">=,值:\n"75",plateValue:"56",id : 0} editCondition.jsx:153 STATE SET!\nupdateExpression 条件: {id: "1", group: 1, name: "Temperature >=\n75F", meta: "如果温室中 >= 75F 打开空调直到 5比 75F 冷\n度”,表达式:Array(1)}
\n我在那里看到一个“plateValue:“56””。那么为什么它不在子组件中重新渲染呢?如此迷茫。
\n
我尝试过 componentWillReceiveProps、componentWillUpdate 等。我什至无法让它们触发console.log。
\n发生了一些我无法弄清楚的事情。我已经使用 React 很长时间了,但我被难住了。这种情况不再经常发生了。
\n在此先感谢您的帮助
\nPS 我确实查看了 getDerivedStateFromProps - 文档提供了示例,这很好,但它们没有解释 props 和 state 参数实际上是什么。文档很糟糕。他们的解释很糟糕。他们的例子并没有说明它的实际作用。我只使用 componentWillReceiveProps 来了解 prop 何时发生更改,然后更新状态或其他内容。getDerivedStateFromProps 只是让我困惑。尽管如此,我还是玩弄了它,但也无法让它工作。
\n\n看起来好像一直在传递同一个expression 对象。
props在决定渲染时,React 检查组件接收到的更改。它发现所有props项目都没有改变,它们都是与之前相同的对象,并得出结论:子组件不需要重新渲染。它不会对每个道具的所有属性进行深入检查。
这也解释了为什么可以通过复制表达式对象来强制重新渲染。副本始终是一个新对象,因此会导致重新渲染,无论其内容是否已更改。
您可以像已经做的那样避免这种情况,方法是制作一个副本,或者将expression对象分解为其属性,然后将每个属性单独输入props到子对象中。
最后一点,也可以通过将其作为 传递来制作副本expression={{...expression}}。