Добавить данные из вызова извлечения к API в мое состояние - PullRequest
1 голос
/ 04 ноября 2019

У меня есть эта функция ниже, которая запускает функцию внутри цикла forEach, например:

async populateSystemData(id) {
    const response = await fetch('https://localhost:44389/api/systemlist/GetSystems/' + id);
    const data = await response.json();
    const result = Object.values(data);
    result.forEach((s) => {
        this.populatePlanetData(s.systemId);
    });
    this.setState({ systems: result, loading: false });
}

Вот функция, которая выполняется внутри цикла:

async populatePlanetData(id) {
    const response = await fetch('https://localhost:44389/api/planetlist/GetPlanets/' + id);
    const data = await response.json();
    const result = Object.values(data);
    this.setState({ planets: result, loading: false });
}

Вот как я записываю эти данные, чтобы пользователь мог их увидеть:

{systems.map(s =>
    <tr>
        <td>{s.SystemName}</td>
        <td>
            <table>
                <tr>
                    <td>Planets</td>
                </tr>
                {planets.map(p =>
                    <tr>
                        <td>{p.PlanetName}</td>
                    </tr>
                )}                                      
            </table>
       </td>
    </tr>
 )}

Какой лучший способ обновить состояние внутри populatePlanetData, чтобы оно добавлялось, а не перезаписывалось?

Спасибо!

Ответы [ 3 ]

2 голосов
/ 04 ноября 2019
async populateSystemData(id) {
    const response = await fetch(`https://localhost:44389/api/systemlist/GetSystems/${id}`);
    const data = await response.json();
    const systems = Object.values(data);

    this.setState({ systems, loading: false });

    // good way to get async list data
    const planetsData = await Promise.all(systems.map(async (system) => {
      const res = await fetch(`https://localhost:44389/api/planetlist/GetPlanets/${system.systemId}`);
      const planetData = await res.json();

      return planetData;
    }));
    const planets = planetsData.map(planet => Object.values(planet));

    this.setState({ planets, loading: false });
}
2 голосов
/ 04 ноября 2019

Другой подход к более эффективному использованию компонентов React

, основанный на комментарии авторов

there could be 200+ systems, with each system having 1 and up to 50 planets

Вместо циклического перебора функций и наличия одного глобального состояния для загрузки рассмотрите возможность разделения на компоненты.

Концептуально нам нужны три компонента

  • Системы - загрузка всех систем и отображение всех систем
  • Система - загрузка всех планет из систем и отображения планет
  • Планета- Показать содержимое планеты

Давайте продолжим и внедрим их

  1. Системы
function Systems({ id }) {
  const [systems, setSystems] = useState([]);

  useEffect(() => {
    const getSystems = async () => {
      const response = await fetch(
        `https://localhost:44389/api/systemlist/GetSystems/${id}`,
      );
      const data = await response.json();
      setSystems(Object.values(data));
    };
    getSystems();
  }, [id]);

  return (
    <table>
      {systems.map((system) => (
        <System system={system} />
      ))}
    </table>
  );
}
Система
function System({ system: { id, SystemName } }) {
  const [planets, setPlanets] = useState([]);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const getPlanets = async () => {
      const response = await fetch(
        `https://localhost:44389/api/systemlist/GetSystems/${id}`,
      );
      const data = await response.json();
      setPlanets(Object.values(data));
      setIsLoading(false);
    };
    getPlanets();
  }, [id]);

  return (
    <tr>
      {isLoading && <td>Loading...</td>}
      {!isLoading && (
        <>
          <td>{SystemName}</td>
          <td>
            <table>
              <tr>
                <td>Planets</td>
              </tr>
              {planets.map((planet) => (
                <Planet planet={planet} />
              ))}
            </table>
          </td>
        </>
      )}
    </tr>
  );
}
Планета
const Planet = ({ planet: { PlanetName } }) => (
  <tr>
    <td>{PlanetName}</td>
  </tr>
);

Что вы думаете об этом подходе?

1 голос
/ 04 ноября 2019

Вы можете сделать это, распространяя значения oldState

this.setState(({ planets }) => ({ planets: [...planets, ...result], loading: false }));

В качестве небольшой заметки - вам не следует устанавливать загрузку в populatePlanetData. Лучше используйте карту вместо forEach и поместите await Promise.all(result.map(...)) вокруг нее, после того, как вы дождались всех асинхронных операций, вы можете перейти и установить состояние загрузки false.

ОБНОВЛЕНИЕ с изменениями из обсуждения в ответе от @zemil

async populateSystemData(id) {
  const api = `https://localhost:44389/api`;
  const response = await fetch(`${api}/systemlist/GetSystems/${id}`);
  const data = await response.json();
  const systems = Object.values(data);

  this.setState({ systems, loading: false });

  // good way to get async list data
  const planetsData = await Promise.all(
    systems.reduce(async (allSystemsPromise, system) => {
      const allSystems = await allSystemsPromise;
      const res = await fetch(
        `${api}/planetlist/GetPlanets/${system.systemId}`,
      );
      const planetData = await res.json();

      return [...allSystems, ...system];
    }, Promise.resolve([])),
  );
  const planets = planetsData.map((planet) => Object.values(planet));

  this.setState({ planets, loading: false });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...