0

总的来说,我对 JavaScript/React 非常陌生,并且在和 的概念上苦苦Promise挣扎async

首先,我有getSimById一个 JS 文件中的 API 调用,它返回一个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.");
}

现在我有一个返回的功能组件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]它实际上返回的东西时undefined,我都无法通过它在Table. 在这种情况下,如何将 Promise 分配给变量,以便对其执行操作?

4

3 回答 3

1

好的,所以您的 api 调用按预期工作并且您收到

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

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

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

但是你的问题在let这里。

一个可能的解决方案可能是:

在组件之外(也许是 utils)?

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>)
}
于 2020-08-18T17:10:17.430 回答
1

您不应该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>
  );
}

还有另一个问题:如果你的效果在它们到达之前再次运行,你想忽略异步获得的结果。为此,您从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 的这篇文章提供了一些关于钩子的一般信息useEffect,特别是关于钩子的信息。

于 2020-08-18T17:07:28.160 回答
0

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

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

于 2020-08-18T17:04:08.643 回答