Как сделать заново, если ключ остается прежним, но другие значения меняются? - PullRequest
0 голосов
/ 18 марта 2019

Я пишу приложение 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;

1 Ответ

1 голос
/ 18 марта 2019

Хорошо, теперь я вижу это.Единственная опора, которую вы передаете renderBodyCell, это key, никаких других опор.Это плохая практика (и просто неправильная).key s используются как внутренние подсказки по оптимизации для реагирования и не должны использоваться для реквизита.

  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 (какие функциональные компоненты).

Поэтому, если вы хотите что-то обновить, убедитесь, что пропускаемые вами реквизиты изменились.

...