如果键保持不变,但其他值发生变化,如何重新渲染?
How to rerender, if key stays the same, but other values change?
我正在编写一个 React 应用程序。我有 table 个联系人:
// ... pure functional component that gets the contacts via props
return (
<Paper>
<table>
<thead>
<tr>
{fields.map(renderHeaderCell)}
</tr>
</thead>
<tbody>
{contacts.map(renderBodyRow)}
</tbody>
</table>
</Paper>
);
renderBodyRow()
函数如下所示:
const renderBodyRow = contact => (
<ContactRow
key={contact.id}
contact={contact}
handleContactSave={handleContactSave}
/>
);
现在,当我更新联系人并且未对 table 进行排序时,联系人会向下移动到列表底部。但不是使用更新后的名称呈现,而是使用旧名称呈现。我认为这是因为 contact.id
键没有改变。我怎样才能让行呈现新值?
为了完整起见(并且因为它可能会导致问题),这里是 ContactRow
组件。我觉得问题不在这里 thought
import PropTypes from 'prop-types';
import { equals, includes, map } from 'ramda';
import React, { useState } from 'react';
import { fields, groups, tendencies } from '../../config/constants';
import strings from './strings';
function ContactRow({ contact: original, handleContactSave }) {
const [contact, setContact] = useState(original);
const disabled = equals(contact, original);
const handleSaveButtonClick = () => {
handleContactSave(contact);
setContact(original)
};
const handeCancelButtonClick = () => {
setContact(original);
};
const renderOption = value => (
<option key={`${contact.id}-${value}`} value={value}>
{strings[value]}
</option>
);
const renderBodyCell = key => {
const value = contact[key];
const testId = `contact-${key}${
contact.id === 'new-contact' ? '-new-contact' : ''
}`;
const handleChange = e => {
e.preventDefault();
setContact({ ...contact, [key]: e.target.value });
};
return (
<td key={`${key}-${contact.id}`}>
{includes(value, [...groups, ...tendencies]) ? (
<select value={value} data-testid={testId} onChange={handleChange}>
{includes(value, groups)
? map(renderOption, groups)
: map(renderOption, tendencies)}
</select>
) : (
<input value={value} data-testid={testId} onChange={handleChange} />
)}
</td>
);
};
return (
<tr>
<td>
<button
aria-label={
contact.id === 'new-contact' ? 'create-contact' : 'update-contact'
}
onClick={handleSaveButtonClick}
disabled={disabled}
>
<span role="img" aria-label="save-icon">
</span>
</button>
<button
aria-label={
contact.id === 'new-contact'
? 'cancel-create-contact'
: 'cancel-update-contact'
}
disabled={disabled}
onClick={handeCancelButtonClick}
>
<span role="img" aria-label="cancel-icon">
</span>
</button>
</td>
{map(renderBodyCell, fields)}
</tr>
);
}
ContactRow.propTypes = {
contact: PropTypes.shape({
/* fields */
}),
handleContactSave: PropTypes.func.isRequired
};
ContactRow.defaultProps = {
contact: fields.reduce((acc, field) => ({ ...acc, [field]: 'N/A' }), {}),
handleContactSave: () => {
console.warn('No handleContactSave() function provided to ContactRow.');
}
};
export default ContactRow;
好的,我现在看到了。您传递给 renderBodyCell
的唯一道具是 key
,没有其他道具。这是不好的做法(而且是错误的)。 key
s 用作内部优化提示以做出反应,不应用于 props。
const renderBodyCell = key => {
const value = contact[key];
const testId = `contact-${key}${
contact.id === 'new-contact' ? '-new-contact' : ''
}`;
const handleChange = e => {
e.preventDefault();
setContact({ ...contact, [key]: e.target.value });
};
return (
<td key={`${key}-${contact.id}`}>
{includes(value, [...groups, ...tendencies]) ? (
<select value={value} data-testid={testId} onChange={handleChange}>
{includes(value, groups)
? map(renderOption, groups)
: map(renderOption, tendencies)}
</select>
) : (
<input value={value} data-testid={testId} onChange={handleChange} />
)}
</td>
);
};
而不是传递密钥,你需要传递 contact
(或者我猜是联系人和密钥,但我会犹豫传递密钥,就好像它们是有意义的,除非你确切地知道你在做什么)。
编辑:
所以从技术上讲,你是对的,该行没有被重新渲染,因为键没有改变,但那是因为你在不应该的时候将它用作道具。
编辑#2:
是时候探索 React 的工作原理了。这是一个非常优化的机器。它不会一直重新渲染组件,仅在需要时才重新渲染。为了找出何时需要重新渲染它们,它会检查道具和状态(或者,在您以功能方式执行此操作的情况下,仅检查道具 - 函数参数)并将它们与上次组件渲染时的道具进行比较呈现。如果道具相同(浅等于),那么反应只是说搞砸了,我不需要更新,道具是相同的。至少那是 PureComponent
的行为(功能组件是)。
因此,如果您想要更新某些内容,请确保您传递给它的道具已更改。
我正在编写一个 React 应用程序。我有 table 个联系人:
// ... pure functional component that gets the contacts via props
return (
<Paper>
<table>
<thead>
<tr>
{fields.map(renderHeaderCell)}
</tr>
</thead>
<tbody>
{contacts.map(renderBodyRow)}
</tbody>
</table>
</Paper>
);
renderBodyRow()
函数如下所示:
const renderBodyRow = contact => (
<ContactRow
key={contact.id}
contact={contact}
handleContactSave={handleContactSave}
/>
);
现在,当我更新联系人并且未对 table 进行排序时,联系人会向下移动到列表底部。但不是使用更新后的名称呈现,而是使用旧名称呈现。我认为这是因为 contact.id
键没有改变。我怎样才能让行呈现新值?
为了完整起见(并且因为它可能会导致问题),这里是 ContactRow
组件。我觉得问题不在这里 thought
import PropTypes from 'prop-types';
import { equals, includes, map } from 'ramda';
import React, { useState } from 'react';
import { fields, groups, tendencies } from '../../config/constants';
import strings from './strings';
function ContactRow({ contact: original, handleContactSave }) {
const [contact, setContact] = useState(original);
const disabled = equals(contact, original);
const handleSaveButtonClick = () => {
handleContactSave(contact);
setContact(original)
};
const handeCancelButtonClick = () => {
setContact(original);
};
const renderOption = value => (
<option key={`${contact.id}-${value}`} value={value}>
{strings[value]}
</option>
);
const renderBodyCell = key => {
const value = contact[key];
const testId = `contact-${key}${
contact.id === 'new-contact' ? '-new-contact' : ''
}`;
const handleChange = e => {
e.preventDefault();
setContact({ ...contact, [key]: e.target.value });
};
return (
<td key={`${key}-${contact.id}`}>
{includes(value, [...groups, ...tendencies]) ? (
<select value={value} data-testid={testId} onChange={handleChange}>
{includes(value, groups)
? map(renderOption, groups)
: map(renderOption, tendencies)}
</select>
) : (
<input value={value} data-testid={testId} onChange={handleChange} />
)}
</td>
);
};
return (
<tr>
<td>
<button
aria-label={
contact.id === 'new-contact' ? 'create-contact' : 'update-contact'
}
onClick={handleSaveButtonClick}
disabled={disabled}
>
<span role="img" aria-label="save-icon">
</span>
</button>
<button
aria-label={
contact.id === 'new-contact'
? 'cancel-create-contact'
: 'cancel-update-contact'
}
disabled={disabled}
onClick={handeCancelButtonClick}
>
<span role="img" aria-label="cancel-icon">
</span>
</button>
</td>
{map(renderBodyCell, fields)}
</tr>
);
}
ContactRow.propTypes = {
contact: PropTypes.shape({
/* fields */
}),
handleContactSave: PropTypes.func.isRequired
};
ContactRow.defaultProps = {
contact: fields.reduce((acc, field) => ({ ...acc, [field]: 'N/A' }), {}),
handleContactSave: () => {
console.warn('No handleContactSave() function provided to ContactRow.');
}
};
export default ContactRow;
好的,我现在看到了。您传递给 renderBodyCell
的唯一道具是 key
,没有其他道具。这是不好的做法(而且是错误的)。 key
s 用作内部优化提示以做出反应,不应用于 props。
const renderBodyCell = key => {
const value = contact[key];
const testId = `contact-${key}${
contact.id === 'new-contact' ? '-new-contact' : ''
}`;
const handleChange = e => {
e.preventDefault();
setContact({ ...contact, [key]: e.target.value });
};
return (
<td key={`${key}-${contact.id}`}>
{includes(value, [...groups, ...tendencies]) ? (
<select value={value} data-testid={testId} onChange={handleChange}>
{includes(value, groups)
? map(renderOption, groups)
: map(renderOption, tendencies)}
</select>
) : (
<input value={value} data-testid={testId} onChange={handleChange} />
)}
</td>
);
};
而不是传递密钥,你需要传递 contact
(或者我猜是联系人和密钥,但我会犹豫传递密钥,就好像它们是有意义的,除非你确切地知道你在做什么)。
编辑: 所以从技术上讲,你是对的,该行没有被重新渲染,因为键没有改变,但那是因为你在不应该的时候将它用作道具。
编辑#2:
是时候探索 React 的工作原理了。这是一个非常优化的机器。它不会一直重新渲染组件,仅在需要时才重新渲染。为了找出何时需要重新渲染它们,它会检查道具和状态(或者,在您以功能方式执行此操作的情况下,仅检查道具 - 函数参数)并将它们与上次组件渲染时的道具进行比较呈现。如果道具相同(浅等于),那么反应只是说搞砸了,我不需要更新,道具是相同的。至少那是 PureComponent
的行为(功能组件是)。
因此,如果您想要更新某些内容,请确保您传递给它的道具已更改。