将 Promise 分配给 React 中的变量

Assign Promise To Variable In React

总的来说,我对 JavaScript/React 很陌生,对 Promiseasync 的概念感到困惑。

首先我有 getSimById,一个在 JS 文件中的 API 调用,其中 returns 一个 Promise:

export function getSimById(simId) {
  return fetch(simsUrl + "/results/" + simId, {
    method: "GET",
    headers: new Headers({
      Authorization: "Basic " + base64.encode(login + ":" + password)
    })
  })
    .then(handleResponse)
    .catch(handleError);
}

handleResponse是一个异步函数。

export async function handleResponse(response) {
  if (response.ok) {
    let someResponse = response.json();
    return someResponse;
  }

  if (response.status === 400) {
    throw new Error(error);
  }

  const error = await response.text();
  throw new Error("Network response was not ok.");
}

现在我有一个功能组件 returns a Table:

import React, { useState, useEffect } from "react";
import { getSimById } from "../api/outrightSimulatorApi";

function SimulationReport(props) {

  const location = useLocation();
  const [simResult, setSimResult] = useState([]);

  useEffect(() => {
    getSimById(location.state.simId).then(result => setSimResult(result));
  }, []);

  let reformattedData = getSimById(location.state.simId).then(
    data => reformattedData = data?.markets?.length ? data.markets.reduce(
      (accumulator, market) =>
        market.selections.map(({ name, probability }, index) => ({
          ...accumulator[index],
          "Team name": name,
          [market.name]: probability,
        })),
      [],
    ) : null);

  return (
      <div>
          <Table striped bordered hover size="sm" responsive>
            <thead>
              <tr>{

              }
              </tr>
            </thead>
            <tbody>{

             }
            </tbody>
          </Table>
      </div>
  );

在这段代码中,我想将 reformattedData 作为数组进行映射,并最终将其值映射到返回的 Table 中。但是,reformattedData 在这个实例中不是一个数组,实际上是一个 Promise。因此,每当我尝试访问 reformattedData[0] 之类的内容时,它实际上是 returns undefined,并且我无法在 Table 中映射它的值。在这种情况下,如何将 Promise 分配给一个变量,以便我可以对其执行操作?

在您的 useEffect 中,您已经在调用 getSimById() 并存储结果,因此无需立即再次调用它。

相反,请尝试遍历 simResult 数组。那应该具有您要引用的值。

你不应该在两个不同的地方调用 getSimById,它应该只在 useEffect 回调中,它应该将 location.state.simId 列为依赖项。

大致如下:

function SimulationReport(props) {

  const location = useLocation();
  const [simResult, setSimResult] = useState([]);

  useEffect(() => {
    getSimById(location.state.simId).then(data => {
        const reformattedData = data?.markets?.length ? data.markets.reduce(
          (accumulator, market) =>
            market.selections.map(({ name, probability }, index) => ({
              ...accumulator[index],
              "Team name": name,
              [market.name]: probability,
            })),
          [],
        ) : null;
        setSimResult(reformattedData); // *** Set state here
      })
      .catch(error => {
        // *** Handle/report error
      });
  }, [location.state.simId]); // *** Note the dependency

  return (
      <div>
          <Table striped bordered hover size="sm" responsive>
            <thead>
              <tr>{

              }
              </tr>
            </thead>
            <tbody>{
              // *** Use `simResult` when rendering
              simResult.map(entry => <markup>for entry</markup)
             }
            </tbody>
          </Table>
      </div>
  );
}

还有另一个问题:如果您的效果在到达之前再次 运行,您希望忽略异步获得的结果。为此,您 return 来自 useEffect 回调的函数,以便 React 可以在它发生时告诉您,如下所示:

  useEffect(() => {
    let cancelled = false; // ***
    getSimById(location.state.simId).then(data => {
        if (cancelled) {
            // Don't use it
            return;
        }
        const reformattedData = data?.markets?.length ? data.markets.reduce(
          (accumulator, market) =>
            market.selections.map(({ name, probability }, index) => ({
              ...accumulator[index],
              "Team name": name,
              [market.name]: probability,
            })),
          [],
        ) : null;
        setSimResult(reformattedData);
      })
      .catch(error => {
        // Handle/report error
      });
      return () => {        // *** A callback React will use when the effect runs again
        cancelled = true;   // *** Remember that *this* call has been cancelled
      };
  }, [location.state.simId]);
Dan Abramov 的

This article 提供了一些关于钩子的优秀信息,特别是 useEffect

好的,所以您的 api 通话正常,您收到

useEffect(() => {
    getSimById(location.state.simId).then(result => setSimResult(result));
  }, []);

同时解析数据可以这样简化

getSimById(location.state.simId).then(parseSimResult);

但是你的问题出在let这里。

可能的解决方案是:

超出组件(可能是实用程序)?

export const parseSimResults = (simResults) => {
  return simResults.markets.reduce(
      (accumulator, market) =>
        market.selections.map(({ name, probability }, index) => ({
          ...accumulator[index],
          "Team name": name,
          [market.name]: probability,
        })),
      [],
    )
}; 

然后就在渲染图中抛出 simResults 在你的组件渲染中

<thead>
  {simResult && simResults.map(r => {
    <tr key="someKEY">
         {
          ...
         }
    </tr>
  })}
</thead>

生成完整代码

const parseSimResults = (simResults) => {
  return simResults.markets.reduce(
      (accumulator, market) =>
        market.selections.map(({ name, probability }, index) => ({
          ...accumulator[index],
          "Team name": name,
          [market.name]: probability,
        })),
      [],
    )
}; 

const MyComponent.... {
  const [simResults, setSimResults] = useState([]);

  useEffect(() => {
    getSimById(location.state.simId).then(parseSimResults);
  }, []);

  return simResults.map(r => <your JSX>)
}