Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
SugarTeam opened this issue Oct 18, 2020 · 0 comments
Open

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

SugarTeam opened this issue Oct 18, 2020 · 0 comments
Assignees
Labels
React React

Comments

@SugarTeam
Copy link
Contributor

前言

在日常的开发中,从服务器端异步获取数据并渲染是相当高频的操作。在以往使用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获取数据并渲染组件了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
React React
Projects
None yet
Development

No branches or pull requests

2 participants