【问题标题】:How to make React Hooks return an array如何让 React Hooks 返回一个数组
【发布时间】:2020-09-29 03:20:20
【问题描述】:

我的 React 应用程序中有一些硬编码数据,现在我想从 API 中获取这些数据。我想保留类似数组的格式,因为这就是未来使用数据的方式。

我原来的组件:

import moment from 'moment';

const INITIAL_STATE = {
  articles:
    [
      {
        title: 'Guerrilla Public Service Redux (2017)',
        tag0: 'story',
        points: 421,
        created_at: moment('2020-05-27T16:05:32.000Z'),
        author: 'DerWOK',
      },
      {
        title: 'Build Yourself a Redux',
        tag0: 'story',
        points: 395,
        created_at: moment('2020-05-27T16:05:32.000Z'),
        author: 'jdeal',
      },
    ],
};

function Articles (state = INITIAL_STATE) {
      return state;
}

export default Articles;

这就是我想象的工作方式,但下面的 return() 中应该是什么对我来说是个谜:

import moment from 'moment';

function Articles() {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [items, setItems] = useState([]);

  useEffect(() => {
    fetch("https://hn.algolia.com/api/v1/search?query=redux")
      .then(res => res.json())
      .then(
        (result) => {
          setIsLoaded(true);
          setItems(result.items);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      )
  }, [])

  if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (

        {items.map(item => (
         ???)
    );
  }
}

export default Articles;

编辑:我确实有列表组件来呈现数据。我只需要以可接受的形式返回数据。

【问题讨论】:

  • 这能回答你的问题吗? React.js create loop through Array
  • 你不是想让你的钩子返回一个数组,Fetch(在你的代码中)是一个功能组件,而不是一个钩子。如果您的问题是???,那么您只是试图将一个数组呈现为 JSX 元素。请查看建议的副本。
  • 您的result.item 维护着类似数组的结构。如果您正在谈论将数组结构向下传递给孩子,您可以简单地做到这一点 return (&lt;SomeComponent itemList={items} /&gt;) 而不使用 items.map
  • 但是你在做的不是一个钩子,就像提到的@zero298。您的退货声明与 ???是渲染的返回值,而不是任何其他值。
  • @habr 所以你的方法是错误的,因为你在其他 if 块上返回 JSX。主要问题是你为什么想要一个钩子来做这个,因为我不确定。如果你能回答这个问题,我们可以帮你创建一个合适的钩子(useGetArticles)

标签: arrays reactjs react-hooks


【解决方案1】:

这对我来说看起来不错,除了在最后一个用例和其他用例中没有从组件返回 JSX 之外。试试这个

 return (
         <ul>
        {items.map((item, index) => <li key={index}>{item.title} </li> )}
         </ul>
    );

请注意,如果您的项目具有唯一 id,最好使用它们作为键而不是索引。

编辑: 这是一个示例,说明如何在文章中使用挂钩

function useGetArticles() {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [items, setItems] = useState([]);

  useEffect(() => {
    fetch('https://hn.algolia.com/api/v1/search?query=redux')
      .then((res) => res.json())
      .then(
        (result) => {
          setIsLoaded(true);
          setItems(result.items);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, []);

  return [items, isLoaded, error];
}

function Articles() {
  const [items, isLoaded, error] = useGetArticles();

  if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (
      <ul>
        {items.map((item, index) => (
          <li key={index}>{item.title} </li>
        ))}
      </ul>
    );
  }
}

【讨论】:

  • @habr 您的初始代码没有用于反应挂钩的导入。可能也想添加。
  • 我不想返回列表,只返回列表的数据。
  • @habr 我认为您需要重新考虑在不同情况下返回的内容。在某些情况下返回 JSX 而在其他情况下返回对象数组有点令人困惑。你可以让它工作,但我认为它是不可管理的。对不起,如果我错过了任务的重点
  • ConnorRobertson 和@dabishan,非常感谢您的时间和帮助。根据您的建议,我决定重新考虑整个事情并决定使用 redux。你可以在这里看到代码:stackoverflow.com/questions/62503354/…
【解决方案2】:

如果您要做的只是将 Articles 作为一个函数来维护,那么您必须将其作为一个 Promise(这是多余的,因为 fetch 正在做您需要做的事情)。

您可以按照自己的方式编写自定义钩子,但是 Articles(必须命名为 useArticles,因为 react 需要它)不能成为您想要的功能,并且您将无缘无故地复杂化。

我的建议是像这样简单地将 useEffect 和其他状态挂钩移动到您的父组件(注意 App 是您的父组件而不是 Articles):

import React, {useState, useEffect} from 'react';

const App = () => {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [items, setItems] = useState([]);

  useEffect(() => {
    Articles()
        .then(res => {
        setIsLoaded(true);
        setItems(res.hits);
        setError(false);
      })
      .catch(error => {
        setIsLoaded(true);
        setError(true);
      })
    // you are repeating then/catch again even though you were doing it already on fetch. Only good reason to do this is if you are reusing the api calls in other places.
  }, [])

   return (
    <React.Fragment>
      Test
      <ul>
       {items.map((item, index) => <li key={index}>{item.title} </li> )}
     </ul>
    </React.Fragment>

    );

}

function Articles() {
  // too much code just to hide it inside a function
    return new Promise((resolve, reject) =>
    fetch("https://hn.algolia.com/api/v1/search?query=redux")
    .then(res => resolve(res.json()))
    .catch(error => reject(error))
  );
}

export default App;

这是重新格式化代码的钩子方法。但这本身并不意味着它更好:

import React, {useState, useEffect} from 'react';

const App = () => {
  // getting all useful state from the hook
  const {error, isLoaded, items, fetchItems} = useGetArticles();

  useEffect(() => {
    fetchItems(); // calling the method that ask for new data and loads item in the hook's state
  })

   return (
    <React.Fragment>
      <ul>
       {items.map((item, index) => <li key={index}>{item.title} </li> )}
     </ul>
    </React.Fragment>

    );

}
const useGetArticles = () => {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [items, setItems] = useState([]);

  const fetchItems = () => {
    fetch("https://hn.algolia.com/api/v1/search?query=redux")
    .then(res => res.json())
    .then(res => {
      setIsLoaded(true);
      setItems(res.hits);
      setError(false);
    })
    .catch(error => {
      setIsLoaded(true);
      setError(true);
    })
  };

  // only return useful states and functions that main component can use
  return {error, isLoaded, items, fetchItems}
}

export default App;

【讨论】:

  • 非常感谢@dabishan。我想要么我设法在这里返回数组,要么需要使用 Redux。第一个选项听起来更简单。如果它不打扰你,如果你能添加钩子版本,我将不胜感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多