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
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。編程
這裏,咱們將再也不使用 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 獲取數據。
在這以前, 咱們都是在 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> ); }
運行一下就會發現,哎呦, 報錯了, 無限循環:
緣由其實也很是簡單, 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.
不管怎麼說, useEffect 的出現仍是一件好事。
能顯然是能的, 不過沒什麼意義, 好比把上面的代碼改一下:
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 自己就是爲了解決組件間邏輯公用的問題的。
仍是上面的例子,咱們把取數據的邏輯抽出來:
// 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> ); }
上面咱們說到 Reack Hooks 自己就是爲了解決組件間邏輯公用的問題的。
回顧咱們如今的作法,幾乎都是面向生命週期編程:
Hooks 的出現是把這種面向生命週期編程變成了面向業務邏輯編程,讓咱們不用再去關注生命週期:
並且, 最新的React 中, 預置了大量的Hooks, 最重要兩個的就是: useState
and useEffect
.
useState 使咱們在不借助 ES6 class 的前提下, 在組件內部使用 state 成爲可能
。
useEffect 取代了 componentDidMount, componentDidUpdate, and componentWillUnmount
, 提供了一個統一的API
。
除了這兩個以外, 能夠在官方文檔中瞭解更多:
一個顯而易見的事實是, 過不來了多久, 咱們就會有三種建立React components 的姿式:
做爲一個 React 忠實粉絲, 看到這些積極的變化實在是使人感到愉悅。
還有不少幫助咱們更好的學和掌握 React Hooks, 也在這裏分享一下:
首先仍是官方文檔: Introducing Hooks, Hooks 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 在社區中的影響力( 其實這兩個東西的核心開發者是同一我的 )。
大概就是這些, 但願能對你們有些啓發和幫助。
才疏學淺,行文如有紕漏,還請各位大大幫忙指正, 謝謝。