【问题标题】:Use multiple React useState() hooks to fetch Category title and posts from json api使用多个 React useState() 挂钩从 json api 获取类别标题和帖子
【发布时间】:2021-01-24 20:28:00
【问题描述】:

我正在使用 React useState() 和 useEffect 从 api (mock-data/my-data.json) 获取多个数据。

我想从 api 中获取 Category Title 和 Posts 列表并在 jsx 中呈现它们。

这是使用useState() 和useEffect() 获取数据的正确方法吗?我创建了多个 useState 常量,因此在我可以在渲染方法中渲染 Title 和列表操作 Posts 之后:

function ArticleList() {
  const [categoryTitle, setCategoryTitle] = useState('')
  const [posts, setPosts] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    setLoading(true)
    axios
      .get('mock-data/my-data.json')
      .then(res => {
        console.log(res.data)
        setCategoryTitle(res.data['title'])
        setPosts(res.data.allItems)
        setLoading(false)
      })
      .catch(err => {
        console.log(err)
      })
  }, [])

  if (loading) {
    return <p>Loading articles...</p>
  }

  return (
    <div>
      <h1>{categoryTitle}</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.titel}</li>
        ))}
      </ul>
    </div>
  )
}

【问题讨论】:

  • 您可以使用多个useStates 和useEffects。你有什么错误吗?
  • 这对我来说看起来不错。
  • 它的概念是对的,但避免直接从组件调用API,最好创建一个分离的可重用操作和服务。

标签: reactjs axios use-effect use-state


【解决方案1】:

如果您想同时设置多个状态,通常使用useReducer 挂钩而不是使用多个useState 挂钩是个好主意。您的示例是您尝试将多个状态设置在一起的情况。 useReducer 使用可以操纵合并状态的减速器功能。在我看来,这使代码更具凝聚力和可读性。 请参阅下面的示例代码。为简单起见,我删除了 axios,但您应该明白了。

https://codesandbox.io/s/bold-platform-hv6pi?file=/src/ArticleList.js

【讨论】:

  • 我对 useReducer 的唯一抱怨是它缺少 thunk。
【解决方案2】:

差不多就是这样,我强烈推荐的唯一方法是使用 await 而不是著名的哈杜肯/厄运金字塔,所以一个简单的代码是:

function ArticleList() {
  const [categoryTitle, setCategoryTitle] = useState('')
  const [posts, setPosts] = useState([])
  const [loading, setLoading] = useState(false)

  useEffect(() => {
    async function getData(){
    try{
        setLoading(true)
        res = await axios.get('mock-data/my-data.json');    
        console.log(res.data)
        setCategoryTitle(res.data['title'])
        setPosts(res.data.allItems)
        setLoading(false)        
    }catch(err){
        console.log(err)
      }
    }
    getData();
    }, [])

  if (loading) {
    return <p>Loading articles...</p>
  }

  return (
    <div>
      <h1>{categoryTitle}</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.titel}</li>
        ))}
      </ul>
    </div>
  )
}

【讨论】:

  • 这行不通。您不能在常规函数中使用 await,也不能将异步回调传递给 useEffect。相反,在你的 useEffect 回调中定义一个异步函数,然后调用它。
  • @Benjamin 是否有可能在初始返回语句中呈现来自:if (loading) { return &lt;p&gt;Loading articles...&lt;/p&gt; } 的返回语句:return ( &lt;div&gt; &lt;h1&gt;{categoryTitle}&lt;/h1&gt; &lt;ul&gt; {posts.map(post =&gt; ( &lt;li key={post.id}&gt;{post.titel}&lt;/li&gt; ))} &lt;/ul&gt; &lt;/div&gt; )
  • @meez 是的,您可以使用三元组来做到这一点。但是,我建议不要这样做。使用提前返回更容易阅读。这也是编写函数时的常见模式。尽可能早点退出/返回。
【解决方案3】:

这对我来说看起来不错。

如果我要重构这段代码,我更喜欢使用 async/await,并且我更喜欢使用状态枚举而不是布尔值来处理加载/加载/错误状态。

function ArticleList() {
  const [categoryTitle, setCategoryTitle] = useState('');
  const [posts, setPosts] = useState([]);
  const [state, setState] = useState('loading');

  useEffect(() => {
    const fetchPosts = async () => {
      setState('loading');

      let response = axios.get('mock-data/my-data.json');

      try {
        response = await response;
      } catch (error) {
        setState('error');
        return;
      }

      setCategoryTitle(response.data['title']);
      setPosts(response.data.allItems);
      setState('loaded');
    };

    fetchPosts();
  }, []);

  if (state === 'loading') {
    return <p>Loading articles...</p>;
  }

  if (state === 'error') {
    return <p>Error loading articles...</p>;
  }

  return (
    <div>
      <h1>{categoryTitle}</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.titel}</li>
        ))}
      </ul>
    </div>
  );
}

【讨论】:

  • 谢谢。只为了解一切。你为什么使用异步/等待?有什么好处?
  • @meez async/await 与链接 promise 方法相比,使您的代码更整洁、更易于阅读和理解。
  • 为什么 setter 函数不在 try { //... } 块中? setCategoryTitle(response.data['title']); setPosts(response.data.allItems); setState('loaded');
  • @mmeez 好问题!通常,我会尽量减少包含在 try...catch 中的内容。在这种情况下,我只想捕获与获取相关的错误。如果在调用 setState 时抛出错误,那么问题就更大了,应用程序可能应该停止。
【解决方案4】:

是的,这是正确的方法。如果你只想使用一个 useState,你可以克隆 state 对象。 例如:

const [state, setState] = useState({
  title: "",
  posts: []
});

// For Title
setState({...state, title: "New Title"})

// For Posts
setState({...state, posts: [{...post...}]}

但我想使用 lodash 克隆、immer 库或类似的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-07
    • 2020-08-28
    • 1970-01-01
    • 1970-01-01
    • 2021-06-22
    • 1970-01-01
    相关资源
    最近更新 更多