首页 > 解决方案 > 如果键保持不变,但其他值发生变化,如何重新渲染?

问题描述

我正在编写一个 React 应用程序。我有一个联系人表:

// ... 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}
 />
);

现在,当我更新联系人并且表格未排序时,联系人会移到列表的底部。但它不是使用更新的名称呈现,而是使用旧名称呈现。我认为这是因为contact.id密钥没有改变。如何获取行以呈现新值?

为了完整起见(并且因为它可能导致问题),这里是ContactRow组件。我不认为问题出在这里

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;

标签: javascriptreactjsrenderrenderermap-function

解决方案


好的,所以我现在看到了。您传递给的唯一道具renderBodyCellkey,没有其他道具。这是不好的做法(而且是错误的)。keys 用作反应的内部优化提示,不应用于道具。

  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 工作原理的好时机。这是一台非常优化的机器。它不只是一直重新渲染组件,仅在需要时才重新渲染。为了找出何时需要重新渲染它们,它会检查 props 和 state(或者,在您以功能方式执行此操作的情况下,仅检查 props - 函数参数)并将它们与组件上次运行时的 props 进行比较呈现。如果 props 是一样的(浅等于),那么 react 就说拧它,我不需要更新,props 是一样的。至少这是PureComponent(哪些功能组件是)的行为。

因此,如果您想要更新某些内容,请确保您传递给它的道具已更改。


推荐阅读