.map在功能性React组件的返回中不起作用

时间:2019-10-04 20:10:54

标签: javascript reactjs react-hooks

我有一个使用Hooks的react组件,我在其中单击一个按钮以对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可以在初始状态下使用,但是一旦将状态设置为新数组就不会使用:

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首先起作用,不再在返回中起作用,而是在函数中起作用。...

我将非常感谢任何人的任何帮助。

2 个答案:

答案 0 :(得分: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多个请求,我不认为黑客新闻会喜欢它,因此可以切片结果或分页。 / p>

答案 1 :(得分:1)

我认为这比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);