React State возвращает несинхронизированные данные

У меня есть компонент ниже, который использует хуки React:

import React, {useState} from 'react';

// import components
import TagManagementRow from './TagManagementRow';

const TagManagementList = (props) => {

  const [tagData, setTagData] = useState(props.data);

  const deleteAction = (id) => {
    // Call to backend to delete tag
    const currentData = [];

    for( var i = 0; i <= tagData.length; i++){

      if (i < tagData.length && tagData[i].id !== id) {
        currentData.push(tagData[i]);
      }

      if (i === tagData.length) setTagData(currentData);
    };
  };

  return (
    <ul className = "tagManagement">
      {tagData.map( (tag,i) => {
        return <TagManagementRow name = {tag.name} key = {i} id = {tag.id} delete = {() => deleteAction(tag.id)} />
      })}
    </ul>
  );
}

export default TagManagementList;

Он отображает 4 дочерних компонента TagManagementRow, каждый из которых имеет кнопку удаления. Когда я нажимаю кнопку удаления, все выглядит хорошо, если я выхожу из измененного состояния на консоль, однако в реальном браузере последний элемент в списке удаляется. Я чувствую, что это какая-то проблема рендеринга/времени, но я не могу понять это. Любая помощь от тех, кто лучше разбирается в крючках, будет принята с благодарностью.

Кстати, вот код компонента TagManagementRow:

import React, { useState } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

const TagManagementRow = (props) => {

  const [editTag, setEdit] = useState(false);
  const [tagName, setTagName] = useState(props.name);
  const [tempName, setTempName] = useState('');

  const handleEdit = (e) => {
    setTempName(e.target.value);
  };

  const switchToEdit = () => {
    setEdit(!editTag);
  }

  const saveEdit = () => {
    setTagName(tempName);
    setTempName('');
    switchToEdit();

  }

  return (
    <li>
        <span>
          {tagName}
          <FontAwesomeIcon icon = {["fas","pen"]} onClick = {switchToEdit} />
        </span>

      <span>
        <FontAwesomeIcon icon = {["fas","trash-alt"]} onClick = {props.delete} />
      </span>
    </li>
  );
}

export default TagManagementRow;

Я не знаю, как ответить на это с помощью функциональных компонентов, но мне кажется, что componentDidUpdate решит эту проблему.

lakerskill 25.02.2019 23:40

Вы могли бы так подумать, и именно так этот код ведет себя в другом месте нашего приложения.

Ben 25.02.2019 23:45

Я предполагаю, что проблема заключается в том, что индекс используется в качестве ключа. У вас есть что-то, что вы можете использовать в качестве уникального идентификатора для каждого элемента данных (например, tag.id)?

Garrett Motzner 26.02.2019 00:02
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
2
3
556
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вместо обновления состояния внутри цикла вы можете использовать filter, чтобы отфильтровать объект с совпадающим идентификатором.

Также убедитесь, что вы используете tag.id как key вместо индекса массива, так как это изменится, когда вы удалите элемент.

const { useState } = React;

const TagManagementList = props => {
  const [tagData, setTagData] = useState(props.data);

  const deleteAction = id => {
    setTagData(prevTagData => prevTagData.filter(tag => tag.id !== id));
  };

  return (
    <ul className = "tagManagement">
      {tagData.map((tag, i) => {
        return (
          <TagManagementRow
            name = {tag.name}
            key = {tag.id}
            id = {tag.id}
            delete = {() => deleteAction(tag.id)}
          />
        );
      })}
    </ul>
  );
};

const TagManagementRow = props => {
  const [editTag, setEdit] = useState(false);
  const [tagName, setTagName] = useState(props.name);
  const [tempName, setTempName] = useState("");

  const handleEdit = e => {
    setTempName(e.target.value);
  };

  const switchToEdit = () => {
    setEdit(!editTag);
  };

  const saveEdit = () => {
    setTagName(tempName);
    setTempName("");
    switchToEdit();
  };

  return (
    <li>
      {tagName}
      <button onClick = {props.delete}>Delete</button>
    </li>
  );
};

ReactDOM.render(
  <TagManagementList data = {[{ id: 1, name: "foo" }, { id: 2, name: "bar" }]} />,
  document.getElementById("root")
);
<script src = "https://unpkg.com/react@16/umd/react.development.js"></script>
<script src = "https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id = "root"></div>

Спасибо, Толле, смена ключа, как вы рекомендовали, решила проблему. Так просто, настоящий момент ладони лица для меня. Спасибо за такое быстрое решение

Ben 25.02.2019 23:49

Другие вопросы по теме