如何使用React Hooks请求数据并渲染

前言

在日常的开发中,从服务器端异步获取数据并渲染是相当高频的操作。在以往使用React Class组件的时候,这种操作我们已经很熟悉了,即在Class组件的componentDidMount中通过ajax来获取数据并setState,触发组件更新。

随着Hook的到来,我们可以在一些场景中使用Hook的写法来替代Class的写法。但是Hook中没有setState、componentDidMount等函数,又如何做到从服务器端异步获取数据并渲染呢?本文将会介绍如何使用React的新特性Hook来编写组件并获取数据渲染。

数据渲染

先来看一个数据渲染的简单demo

import React, { useState } from 'react';

function App() {
  const [data, setData] = useState({ products: [{
    productId: '123',
    productName: 'macbook'
    }] });

  return (
    <ul>
      {data.products.map(i => (
        <li key={i.productId}>
          {i.productName}
        </li>
      ))}
    </ul>
  );
}

export default App;

在demo中,通过useState创建了一个叫data的内部state,该state中有一个产品列表数据保存产品数据。App组件通过data中的products来渲染产品列表数据到页面中。

但现在是写死的一个数据,如果我们期望从服务器端获取数据并渲染,那么就需要在组件渲染完成时fetch服务端数据,然后通过setData去改变state触发渲染。我们接下来准备用axios来获取数据。

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

function App() {
  const [data, setData] = useState({ products: [{
    productId: '123',
    productName: 'macbook'
    }] });

  useEffect(async () => {
    const result = await axios(
      'https://c.com/api/products?date=today',
    );

    setData(result.data);
  });

  return (
    <ul>
      {data.products.map(i => (
        <li key={i.productId}>
          {i.productName}
        </li>
      ))}
    </ul>
  );
}

export default App;

代码中使用到的useEffect就是hook的其中一种,叫effect hook。useEffect会在每次组件渲染的时候触发,我们使用它来获取数据并更新state。但是上面的代码是有缺陷的,你发现了吗?

没错,只要你运行一下,你就会发现程序进入了一个死循环。因为useEffect不仅在组件didMounts的时候被触发了,还在didUpdate的时候被触发了。在useEffect中获取数据后,通过setDate改变state,触发组件渲染更新,从而又进入到了useEffect中,无限循环下去。这并不是我们想要的结果。我们最初想要的,只是希望在didMounts的时候获取一次数据而已。所以,这种情况下,我们必须要给useEffect方法的第二个参数传入一个空[],以使得useEffect中的逻辑只在组件didMounts的时候被执行。

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

function App() {
  const [data, setData] = useState({ products: [{
    productId: '123',
    productName: 'macbook'
    }] });

  useEffect(async () => {
    const result = await axios(
      'https://c.com/api/products?date=today',
    );

    setData(result.data);
  },[]);  //重点

  return (
    <ul>
      {data.products.map(i => (
        <li key={i.productId}>
          {i.productName}
        </li>
      ))}
    </ul>
  );
}

export default App;

虽然看起来这个错误比较低级,但确实比较多人在新上手hook时常常犯的问题。

当然,useEffect第二个参数,也可以传入值。当如果有值的时候,那useEffect会在这些值更新的时候触发。如果只是个空数组,则只会在didMounts的时候触发。

另外,执行这段代码,你会看到控制台警告,Promises and useEffect(async () => ...) are not supported, but you can call an async function inside an effect.。所以如果要使用async,需要修改下写法。

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

function App() {
  const [data, setData] = useState({ products: [{
    productId: '123',
    productName: 'macbook'
    }] });

  useEffect(() => {
    const fetchData = async()=>{
        const result = await axios(
          'https://c.com/api/products?date=today',
        );
        setData(result.data);
    }
    fetchData();
  },[]); 

  return (
    <ul>
      {data.products.map(i => (
        <li key={i.productId}>
          {i.productName}
        </li>
      ))}
    </ul>
  );
}

export default App;
体验优化

一般的应用在某些请求过程的交互设计上,会加上loading来缓解用户焦虑。那在Hook的写法中,如何实现呢?下面将会介绍。

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

function App() {
  const [data, setData] = useState({ products: [{
    productId: '123',
    productName: 'macbook'
    }] });
  const [isLoading, setIsLoading] = useState(false);

  useEffect(() => {
    const fetchData = async()=>{
        setIsLoading(true);
        const result = await axios(
          'https://c.com/api/products?date=today',
        );
        setData(result.data);
        setIsLoading(false);
    }
    fetchData();
  },[]); 

  return (
  {isLoading ? (
        <div>Loading ...</div>
      ) : (
    <ul>
      {data.products.map(i => (
        <li key={i.productId}>
          {i.productName}
        </li>
      ))}
    </ul>
  )};
}

export default App;

这里通过加入一个叫isLoading的state来实现。我们在fetch的开始和结束去改变isLoading的值,来控制return返回的组件内容,从而在请求前显示Loading组件,在请求后显示产品列表。

错误处理

请求的过程经常会由于各种原因失败,比如网络、服务器错误等等。所以错误处理必不可少的。

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

function App() {
  const [data, setData] = useState({ products: [{
    productId: '123',
    productName: 'macbook'
    }] });
  const [isLoading, setIsLoading] = useState(false);
  const [isError, setIsError] = useState(false);

  useEffect(() => {
    const fetchData = async()=>{
        setIsError(false);
        setIsLoading(true);

        try{
            const result = await axios(
              'https://c.com/api/products?date=today',
            );
            setData(result.data);
        }catch(e){
            setIsError(true);
        }
        setIsLoading(false);
    }
    fetchData();
  },[]); 

  return (
  <div>
    {isError && <div>出错了...</div>}
    {isLoading ? (
        <div>Loading ...</div>
      ) : (
    <ul>
      {data.products.map(i => (
        <li key={i.productId}>
          {i.productName}
        </li>
      ))}
    </ul>
  )};
  </div>

}

export default App;

当请求出错时,isError会被设置为true,触发渲染时,错误提示组件就会被渲染出来。这里的处理比较简单,在真实场景中,你可以在错误处理时加入更复杂的逻辑。isError会在每次hook运行的时候被重置。

最后

读到这你已经基本学会了如何使用React Hooks获取数据并渲染组件了。

博客首发于github.com/SugarTurboS…, 欢迎关注。

原文链接:juejin.im

上一篇:看完这篇vue面试key和diff就可以开始表演了 ✨✨✨
下一篇:Vue 总结:组件间的通讯方式(父子传参、兄弟传参、任意两个组件间传参、多个组件嵌套传参)

相关推荐

  • 🌈 React 函数式组件优化

    1. React 性能优化思路 减少重新 render 的次数。 减少计算的量。主要是减少重复计算,对于函数式组件来说,每次 render 都会重新从头开始执行函数调用。

    1 个月前
  • 🆘 一次理解清楚,为什么使用 React useEffect 中使用 setInterval 获取的值不是最新的

    Intro 这篇文章将通过一个使用 React Hook 常遇到的问题(stale state)入手,尝试理解 Hook 的内部运行逻辑。 废话不多说,直接看示例Sandbox。

    3 个月前
  • 高频数据交换下Flutter与ReactNative的对比

    (标题图片来自网络,侵删) 后端使用go写的socketio服务模拟期货行情数据,每10ms推送10条行情数据 ReactNative已经尽力优化了。 Flutter由于没flutter-socket...

    2 年前
  • 高性能迷你React框架 anu1.3.0 发布

    anujs1.3.0是一款高性能React-like框架,是目前世界上对React16兼容最好的迷你库。 自React16起,相继推出createContext,createPortal, creat...

    3 年前
  • 高德地图 react-amap 实战

    react-amap 是基于 React 的高德地图组件。 1. 获取地图示例 react-amap 作为高德地图在 React 中的实现,实际使用中不可避免的需要通过地图对象调用各种方法,reac...

    1 年前
  • 高品质 React UI 组件

    A high quality UI Toolkit, A Component Library for React 16+. 💘 Installation npm install isui --s...

    3 年前
  • 骚操作!在react中使用vuex

    原文地址 前言 笔者最近在学习使用react,提到react就绕不过去redux。redux是一个状态管理架构,被广泛用于react项目中,但是redux并不是专为react而生,两者还需要react...

    2 年前
  • 项目文档说明:react + Ant Design 的 blog-react-admin

    前言 此 blog-react-admin 项目是基于 蚂蚁金服开源的 ant design pro 之上,用 react 全家桶 + Ant Design 的进行再次开发的,项目已经开源,项目地址...

    2 年前
  • 面试题:Hooks 与 React 生命周期的关系

    React 生命周期很多人都了解,但通常我们所了解的都是 单个组件 的生命周期,但针对 Hooks 组件、多个关联组件(父子组件和兄弟组件) 的生命周期又是怎么样的喃?你有思考和了解过吗,接下来我们将...

    1 年前
  • 面试官:请你在React中引入Vue3的@vue/reactivity,实现响应式。

    前言 React的状态管理是一个缤纷繁杂的大世界,光我知道的就不下数十种,其中有最出名immutable阵营的redux,有mutable阵营的mobx,react-easy-state,在hooks...

    9 个月前

官方社区

扫码加入 JavaScript 社区