React Hooks 从入门到上手

Stella981
• 阅读 615

React Hooks 从入门到上手

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.


前言

楼主最近在整理 React Hooks 的一些资料,为项目重构作准备,下午整理成了这篇文章。
如果之前没接触过相关概念,那么通过这篇文章, 你将会了什么是React Hooks , 它是做什么的 , 以及如何使用

下面我会用一个具体的例子来说明, 通过这个例子, 你将了解:

  • 如何使用 React Hooks
  • 如何用 React Class components 实现同样的逻辑

快速开始

先快速搭建一个项目:

npx create-react-app exploring-hooks

Demo in setState

import React, { Component } from "react";

export default class Button extends Component {
  state = { buttonText: "Click me, please" };

  handleClick = () => {
    this.setState(() => {
      return { buttonText: "Thanks, been clicked!" };
    });
  };

  render() {
    const { buttonText } = this.state;
    return <button onClick={this.handleClick}>{buttonText}</button>;
  }
}

功能非常简单: 点一下按钮, 就更新 button 的 text。

Demo in Hooks

这里,我们将不再使用 setState 和 ES6 Class. 轮到我们的Hooks登场了:

import React, { useState } from "react";

引入 useState 就意味着我们将要把一些状态管理置于组件内部, 而且我们的 React Component 将不再是一个 ES6 class, 取而代之的是一个简单的纯函数

引入 useState 之后,我们将从中取出一个含有两个元素的数组:

const [buttonText, setButtonText] = useState("Click me, please");

如果对这个语法有疑问, 可以参考 ES6 解构.

这两个值的名字, 你可以随意取, 和 React 无关,但是还是建议你根据使用的目的取一个足够具体和清晰的名字

就比如上面写的, 一个代表是 buttonText 的 , 另一个代表是 setButtonText 的 更新函数

useState 传入的是一个初始值, 比如, 这个按钮的最初要显示的是: Click me, please。

这个简单的例子的代码全貌:

import React, { useState } from "react";

export default function Button() {
  const [buttonText, setButtonText] = useState("Click me, please");

  function handleButtonClick() {
    return setButtonText("Thanks, been clicked!");
  }

  return <button onClick={handleButtonClick}>{buttonText}</button>;
}

下面我们将介绍如何使用 Hooks 获取数据。

使用 React Hooks 获取数据

在这之前, 我们都是在 componentDidMount 函数里调API:

import React, { Component } from "react";

export default class DataLoader extends Component {
  
state = { data: [] };

  async componentDidMount() {
    try {
      const response = await fetch(`https://api.coinmarketcap.com/v1/ticker/?limit=10`);
      if (!response.ok) {
        throw Error(response.statusText);
      }
      const json = await response.json();
      this.setState({ data: json });
    } catch (error) {
      console.log(error);
    }
  }

  render() {
    return (
      <div>
        <ul>
          {this.state.data.map(el => (
            <li key={el.id}>{el.name}</li>
          ))}
        </ul>
      </div>
    );
  }
}

这种代码大家想必都非常熟悉了, 下面我们用 Hooks 来重写:

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

export default function DataLoader() {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  });

  return (
    <div>
      <ul>
        {data.map(el => (
          <li key={el.id}>{el.title}</li>
        ))}
      </ul>
    </div>
  );
}

运行一下就会发现,哎呦, 报错了, 无限循环:

React Hooks 从入门到上手

原因其实也非常简单, useEffect 存在的目的 和componentDidMount, componentDidUpdate, and componentWillUnmount是一致的, 每次state 变化 或者 有新的props 进来的时候,componentDidUpdate componentDidUpdate` 都会执行。

要解决这个 "bug" 也非常简单, 给 useEffect 传入一个空数组作为第二个参数:

useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  },[]); // << super important array

关于 Hook 的详细信息可以参考: Using the Effect Hook

看到这你可能会按捺不住内心的小火苗,要去重构项目,个人还不建议这么做,因为接下来的几个版本中可能会有变化, 就像Ryan Florence 建议的:

Hooks are not the endgame for React data loading.

Data loading is probably the most common effect in an app.

Don't be in a big hurry to migrate to hooks for data unless you're okay migrating again when suspense for data is stable.

Own your churn.

Ryan Florence (@ryanflorence) February 12, 2019

无论怎么说, useEffect 的出现还是一件好事。

能把 Hooks 用于 Render props 吗

能显然是能的, 不过没什么意义, 比如把上面的代码改一下:

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

export default function DataLoader(props) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch("http://localhost:3001/links/")
      .then(response => response.json())
      .then(data => setData(data));
  }, []); 

  return props.render(data)
}

从外部传入一个render即可, 但是这样做毫无意义: Reack Hooks 本身就是为了解决组件间逻辑公用的问题的。

定义你的 React Hook

还是上面的例子,我们把取数据的逻辑抽出来:

// useFetch.tsx
import { useState, useEffect } from "react";

export default function useFetch(url) {
  const [data, setData] = useState([]);

  useEffect(() => {
    fetch(url)
      .then(response => response.json())
      .then(data => setData(data));
  }, [] );

  return data;
}

在其他组件中引用:

import React from "react";
import useFetch from "./useFetch";

export default function DataLoader(props) {
  const data = useFetch("http://localhost:3001/links/");
  return (
    <div>
      <ul>
        {data.map(el => (
          <li key={el.id}>{el.title}</li>
        ))}
      </ul>
    </div>
  );
}

React Hooks 从入门到上手

React Hooks 的本质

上面我们说到 Reack Hooks 本身就是为了解决组件间逻辑公用的问题的。

回顾我们现在的做法,几乎都是面向生命周期编程:

React Hooks 从入门到上手

Hooks 的出现是把这种面向生命周期编程变成了面向业务逻辑编程,让我们不用再去关注生命周期:

React Hooks 从入门到上手

图片来源

而且, 最新的React 中, 预置了大量的Hooks, 最重要两个的就是: useState and useEffect.

useState 使我们在不借助 ES6 class 的前提下, 在组件内部使用 state 成为可能

useEffect 取代了 componentDidMount, componentDidUpdate, and componentWillUnmount, 提供了一个统一的API

除了这两个之外, 可以在官方文档中了解更多:

React Hooks 从入门到上手

一个显而易见的事实是, 过不来了多久, 我们就会有三种创建React components 的姿势:

  • functional components
  • class components
  • functional components with hooks

作为一个 React 忠实粉丝, 看到这些积极的变化实在是令人感到愉悦。

Hooks 更多学习资源

还有很多帮助我们更好的学和掌握 React Hooks, 也在这里分享一下:

首先还是官方文档: Introducing HooksHooks at a Glance 是稍微深入一些的内容。

然后是一个入门教程: Build a CRUD App in React with Hooks.

关于状态管理, 还有一个比较有趣的文章: useReducer, don't useState

比较有意思的是, 我们最后会大量使用 useReducer, 形势和 Redux 非常类似:

function reducer(state, action) {
  const { past, future, present } = state
  switch (action.type) {
    case 'UNDO':
      const previous = past[past.length - 1]
      const newPast = past.slice(0, past.length - 1)
      return {
        past: newPast,
        present: previous,
        future: [present, ...future],
      }
    case 'REDO':
      const next = future[0]
      const newFuture = future.slice(1)
      return {
        past: [...past, present],
        present: next,
        future: newFuture,
      }
    default:
      return state
  }
}

这也从侧面证明了Redux 在社区中的影响力( 其实这两个东西的核心开发者是同一个人 )。

总结

  • Hooks 的出现简化了逻辑,把面向生命周期编程变成了面向业务逻辑编程,为逻辑复用提供了更多可能。
  • Hooks 是未来的方向。

大概就是这些, 希望能对大家有些启发和帮助。

才疏学浅,行文若有纰漏,还请各位大大帮忙指正, 谢谢。

本文同步分享在 博客“皮小蛋”(SegmentFault)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
1星期前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
6个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这