【问题标题】:.map not working in the return of functional React component.map 在功能性 React 组件的返回中不起作用
【发布时间】:2020-02-03 03:54:36
【问题描述】:

我有一个使用 Hooks 的反应组件,我单击一个按钮以对 Hacker News API 进行 API 调用并将结果推送到一个数组中。然后我将“故事”的状态设置为充满故事的数组。

我有第二个功能由一个按钮触发,该按钮控制台记录“故事”的状态,并且 console.log 是一个返回每个故事标题的 .map。所有这些工作都很好。

如果我尝试在组件的返回中使用 .map,它将不起作用。如果我将“故事”的状态初始化为["test", "test1", "test2"],则.map 可以工作,但是一旦我按下按钮将状态设置为故事数组,.map 就会停止工作。没有错误消息,只是内容消失了。

这里是我导入 React 并设置初始状态的地方,我使用 Axios、Fetch 和 Wretch 进行 API 调用,结果都一样:

import React, { useState, useEffect } from 'react';
const axios = require('axios');
import wretch from "wretch"


function App () {
    const [stories, setStories] = useState(["test", "test2", "test3"]);

这是我触发 API 并设置状态的函数:

function call () {
        let storiesArr = [];
        fetch('http://hacker-news.firebaseio.com/v0/topstories.json')
            .then ((res) => res.json())
            .then ((data) => {
                for (let i = 0; i < 20; i++) {
                    fetch(`http://hacker-news.firebaseio.com/v0/item/${data[i]}.json`)
                    .then((res) => res.json())
                    .then((eachStory) => {
                        storiesArr.push(eachStory);
                    })
                }
            })

这是我用来检查状态是否设置为我认为的状态并确保 .map 在“故事”状态下工作的第二个函数。这对我有用:

    function test () {
        console.log(stories);

       stories.map((each) => {
            return <p>{each.title}</p>
        })
    }

这是我的回报,这里的 .map 确实适用于初始状态,但一旦我将 state 设置为新数组就不能:

return (
           <>
                <h1 onClick={ () => call() } className="click">Fire off API calls</h1>
                <h1 onClick={ () => test() } className="click">Test state of stories/<br/>.map each title</h1>
                <table className="table">
                    <thead>
                        <tr>
                            <td>Table</td>
                        </tr>
                    </thead>
                    <tbody>
                           {
                                stories.map((each, i) => {
                                    return <tr key={i}>
                                            <td>{each.title ? each.title : each}</td>
                                        </tr>
                            })
                           }
                    </tbody>
                </table>

           </>
        );

我无法弄清楚为什么 .map 开始工作,不再在返回中工作,但在函数中工作....

我将非常感谢任何人可以提供的任何帮助。

【问题讨论】:

  • 调用setStories时可能有问题,能否显示调用位置?
  • 同意,您需要在使用 setStories 的地方显示代码。这个问题的整个前提都围绕着它,所以我不知道你为什么把它遗漏了..
  • 我不敢相信我做到了。这是调用函数中的最后一件事,在函数关闭之前但在 .then 之外。对不起。我不知何故在我的复制/粘贴中错过了它。 setStories(storiesArr)
  • @henryfrank fetch 是异步的,请将 setStories 放入最后一个 .then() 中,否则会遇到异步问题
  • 谢谢!这似乎是问题所在。

标签: javascript reactjs react-hooks


【解决方案1】:

您的数据获取看起来有点混乱,您知道可以使用 Promise.all 而不是推送到数组并循环。

我已经添加了check 来查看在设置状态之前组件是否仍然挂载。

const isMounted = useIsMounted();
//other code, I imagine useEfffect
function call() {
  fetch(
    'http://hacker-news.firebaseio.com/v0/topstories.json'
  )
    .then(res => res.json())
    .then(data =>
      Promise.all(
        data.map(item =>
          fetch(
            `http://hacker-news.firebaseio.com/v0/item/${item}.json`
          ).then(res => res.json())
        )
      )
    )
    .then(
      result => isMounted.current && setStories(result)
    );
}

另外:http://hacker-news.firebaseio.com/v0/topstories.json 返回超过 400 个项目,这将导致您对每个项目提出超过 400 个请求,我不认为黑客新闻会理解这一点,所以也许可以对结果进行切片或分页。

【讨论】:

  • 这两个听起来都是很棒的建议。对此,我真的非常感激。我现在要试试 Promise.all。
【解决方案2】:

我认为这更像是一个异步处理问题,而不是 setState 问题。这是一个方便的多合一(简化)示例

import React, { useState } from "react";
import ReactDOM from "react-dom";

// gets list of article ids
const getStoryList = async () => {
  const res = await fetch(
    "https://hacker-news.firebaseio.com/v0/topstories.json"
  );

  return await res.json();
};

// iterates over article list and returns a promise.all
const getStories = (articles, quantity) => {
  return Promise.all(
    articles.slice(0, quantity).map(async article => {
      const storyRes = await fetch(
        `https://hacker-news.firebaseio.com/v0/item/${article}.json`
      );

      return await storyRes.json();
    })
  );
};

// maps your response data
const formatStories = stories =>
  stories.map(({ by, id, url, title = "No Title" }) => ({
    id,
    title,
    url,
    by
  }));

function App() {
  const [stories, setStories] = useState([]);

  const call = async () => {
    // first get list of stories
    const res = await getStoryList();
    // then async request all of the individual articles
    // and push them into a group of promises
    const storiesArr = await getStories(res, 20);
    // then set your state.
    setStories(formatStories(storiesArr));
  };

  return (
    <div className="App">
      <button onClick={call} className="click">
        Fire off API calls
      </button>

      <table className="table">
        <thead>
          <tr>
            <td>Table</td>
          </tr>
        </thead>
        <tbody>
          {stories.map(story => {
            return (
              <tr key={story.id}>
                <td>
                  <a href={story.url}>{story.title}</a> by {story.by}
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

【讨论】:

  • 这正是我所需要的。感谢您的帮助。我真的很感激。
  • 嘿,我真的是通过这个来了解它并投入使用。再次感谢您,这是一个很好的解决方案,我非常感谢您的帮助。
  • :) 我很高兴它对我有所帮助,我记得在工作中构建应用程序时我曾一度思考过这个概念,这确实令人沮丧。不确定是否有帮助,但我经常会使用静态元素构建我想要的东西,以确保我的显示内容正确,然后开始处理异步和业务逻辑部分。它可以帮助我了解我正在尝试做的事情,当我去做复杂的部分时,我已经知道如何/何时触发动作
猜你喜欢
  • 2021-10-10
  • 1970-01-01
  • 2022-06-11
  • 2020-08-09
  • 1970-01-01
  • 1970-01-01
  • 2018-04-13
  • 2019-09-01
  • 1970-01-01
相关资源
最近更新 更多