React Hooks 從入門到上手

clipboard.png

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 , 它是作什麼的 , 以及如何使用html

下面我會用一個具體的例子來講明, 經過這個例子, 你將瞭解:react

  • 如何使用 React Hooks
  • 如何用 React Class components 實現一樣的邏輯

快速開始

先快速搭建一個項目:es6

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登場了:json

import React, { useState } from "react";

引入 useState 就意味着咱們將要把一些狀態管理置於組件內部, 並且咱們的 React Component 將再也不是一個 ES6 class, 取而代之的是一個簡單的純函數segmentfault

引入 useState 以後,咱們將從中取出一個含有兩個元素的數組:api

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

若是對這個語法有疑問, 能夠參考 ES6 解構.數組

這兩個值的名字, 你能夠隨意取, 和 React 無關,可是仍是建議你根據使用的目的取一個足夠具體和清晰的名字app

就好比上面寫的, 一個表明是 buttonText 的 , 另外一個表明是 setButtonText 的 更新函數less

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>
  );
}

運行一下就會發現,哎呦, 報錯了, 無限循環:

clipboard.png

緣由其實也很是簡單, 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>
  );
}

clipboard.png

React Hooks 的本質

上面咱們說到 Reack Hooks 自己就是爲了解決組件間邏輯公用的問題的。

回顧咱們如今的作法,幾乎都是面向生命週期編程:

clipboard.png

Hooks 的出現是把這種面向生命週期編程變成了面向業務邏輯編程,讓咱們不用再去關注生命週期:

clipboard.png

圖片來源

並且, 最新的React 中, 預置了大量的Hooks, 最重要兩個的就是: useState and useEffect.

useState 使咱們在不借助 ES6 class 的前提下, 在組件內部使用 state 成爲可能

useEffect 取代了 componentDidMount, componentDidUpdate, and componentWillUnmount, 提供了一個統一的API

除了這兩個以外, 能夠在官方文檔中瞭解更多:

clipboard.png

一個顯而易見的事實是, 過不來了多久, 咱們就會有三種建立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 是將來的方向。

大概就是這些, 但願能對你們有些啓發和幫助。

才疏學淺,行文如有紕漏,還請各位大大幫忙指正, 謝謝。

相關文章
相關標籤/搜索