Skip to main content

Command Palette

Search for a command to run...

React.js - 太弱的我,把 Hooks 點滿就對了

都 2020 年了,還不趕快用 Hooks 參加勾肥大戰?

Updated
4 min read
React.js - 太弱的我,把 Hooks 點滿就對了

都 2020 年了,還不趕快用 Hooks 參加勾肥大戰?

React 在 16.8 版之後納入 Hook 的功能。

bg2019083104.jpg

前言

去年還在研究所打混,經過老闆再三囑咐。對於額外技術也暫時停止學習。

身為一位學術研究員,不專注於非研究領域的程式發展,也很合邏輯 QQ。

目前 CRA ( create-react-app ) 所使用的版號,也來到 16.12.0,

學習永遠不嫌遲,趁著一年國軍 online,趕緊補一下!

本篇文章僅簡單介紹、筆記,建議大家可以閱讀:

  1. React 介紹 Hook
  2. 阮一峰 - React Hooks 入门教程

1. Hooks 介紹

React 在 2019 年 2 月 Release v16.8.0

該版本推出了 Hooks ,鉤子 (?

可以讓我們在 functional component 進行一些 state 的控管。

儘管 Angular 已經來到 8.3 ...

儘管 Vue 已經來到 2.6 ...

但 Hooks 的名詞,勢必會影響不少生態圈(X

春秋百家爭鳴 大家抄來抄去 到底誰有道理 誰的比較高明

你知道嗎?Hooks 其實就是希望能像鉤子一樣,可以隨手勾取所需工具、物品。

2. Functional Component

建議讀者可以直接拿官方的專案建置練習:create-react-app

在使用之前, Hooks 都是建構在 functional component 上,

因此建議至少先看過長什麼樣子:

import React from 'react';

function App() {
  return (
    <div>
      Hello React With Hooks
    </div>
  );
}

export default App;

當然你也可以選擇使用 ES6 的 const 進行 arrow function 宣告:

import React from 'react';

const App = () => {
  return (
    <div>
      Hello React With Hooks
    </div>
  );
}

export default App;

注意! 好你現在看過了,可以繼續看下去囉!

3. Hooks 鉤鉤種類

React 的 Hooks 基本上常見的包含:

  • useState
  • useContext
  • useReducer ( 本篇不介紹,不是懶,是忙投票 )
  • useEffect ( 本篇不介紹,不是懶,是忙投票 )

useState

跟 setState 是同樣意思的,useState 內直接放入初始值即可。

返回陣列的第一個為當前值,第二個 setUser 則是用於設定。

import React, { useState } from 'react';

function App() {

  const [user, setUser] = useState('Anonymous');

  return (
    <div>
      <h1 onClick={() => setUser('Robby')}>
        Hi,
        <small>{user}</small>
      </h1>
    </div>
  );
}

export default App;

你知道嗎?設定值的變數名稱,以最佳實踐來說,大多使用 setUser, setData 等,駝峰式的命名。

useContext

如果我們要從父組件傳值給子子孫孫等組件,通常要 props 很多層,

而你的專案可能又包了一堆 HOC,例如:

export default withTranslation()(connect(mapStateToProps)(Todo));

嚴重時會破壞你的結構,大 guy 會變成下麵這葛樣子, ( 俗稱 Wrapper Hell ):

DoD6dSSVAAAvdkO

如今!!!

只要你會丟出鉤子,稍微彎拉一下...

1578899498_3868.gif

1578899530_4571.gif

就能實現並解決這葛垢病!

請搭配著下方 createContext 章節閱讀。

createContext

在使用 useContext 之前,要先認識 createContext,它是用來創建 Context 的 Provider 。

Context ???

Provider ???

以下使用常見的套件來比喻:

  1. react-redux 的 Provider
  2. styled-components 的 ThemeProvider

如果這比喻不太明白,簡單說就是要從頂部灌水泥,灌資料下去喇 QQ

筆者簡單舉例父子組件傳遞值範例:

import React from 'react';

const name = 'Robby';

function App() {

  return (
    <div>
      <User name={name} />
    </div>
  );
}

function User(props) {
  return (
    <div>{props.name}</div>
  );
}

export default App;

現在將試著使用 createContex 改寫看看:

引入 createContex,宣告變數 UserContext,其參數就是初始值。

import React, { createContext } from 'react';

const name = 'Robby';

const UserContext = createContext(name);

接著於父子組件中使用:

在 UserContext 中,提供了 Provider 以及 Consumer,

  1. Provider:用於父層,並利用 value,來塞入指定的初始值。
  2. Consumer:顧名思義,就是用於接收子組件,內容為回傳一個 function。

請務必命名為 value,不可為其他屬性名稱。

function App() {
  return (
    <UserContext.Provider value={name}>
      <User />
    </UserContext.Provider>
  );
}

function User() {
  return (
    <UserContext.Consumer>
      {name => name}
    </UserContext.Consumer>
  );
}
  • Provider 內需放入一個指定的 component,因此這邊建立一個 User 組件。
  • Consumer 內需放入一個 function,因此這邊使用一個簡易的 arrow function,

過度謹慎的你可能會想改寫...

1578706954_85843.png

沒錯,你也可以拉出來再寫一個 const arrow function。

function User() {

  const renderName = name => {
    return name;
  }

  return (
    <UserContext.Consumer>
      {renderName}
    </UserContext.Consumer>
  );
}

需要注意的是,不可以在 Consumer 同層額外寫其他東西。

但你可以在 Consumer 組件中寫其他元素。

過度謹慎的你可能會不相信...

1578705650_64386.png

沒錯,如果你用以下方式寫,是有問題的。

return (
  <UserContext.Consumer>
    {renderName}
    <div>123</div>
  </UserContext.Consumer>
);

沒錯,如果你用以下方式寫,是沒問題的。

return (
  <div>
    <p>Hi, my name is</p>
    <UserContext.Consumer>
      {renderName}
    </UserContext.Consumer>
  </div>
);

-

過度謹慎的你可能會越想越不對勁...

1578704598_81701.png

一開始已經在 createContext 設定過初始值:

const UserContext = createContext(name);

為何在又要在 UserContext.Provider 內設定 value 初始值?

<UserContext.Provider value={name}>

:「沒錯親愛的,是多寫了。」

如果父層省略掉 Provider ...,

那就是直接以當初 createContext 的初始值為主,

否則會優先以 Provider 的 value 為主。

import React, { createContext } from 'react';

const name = 'Robby';

const UserContext = createContext(name);

function App() {
  return (
    <User />
  );
}

function User() {
  return (
    <UserContext.Consumer>
      {name => name}
    </UserContext.Consumer>
  );
}

export default App;

所以上述範例才故意這樣寫兩次。

q7Dr5pR.png

-

過度謹慎的你可能會想省時間...

1578706445_77621.png

每次都寫 UserContext.Consumer 也太大坨麻煩了吧?

:「沒錯親愛的,終於可以使用 useContext 了!」

useContext 必須放入經由 createContext 所建立的變數。

由於當初設定的 name 為字串,因此勾出來的時候,當然也就是字串囉!

import React, { createContext, useContext, Fragment } from 'react';

const name = 'Robby';

const UserContext = createContext(name);

function App() {
  return (
    <User />
  );
}

function User() {
  const name = useContext(UserContext);
  return (
    <Fragment>
      {name}
    </Fragment>
  );
}

export default App;

你知道嗎? 組件不想用任何元素包住子元素時, 可以使用 Fragment (片段) ,避免冗余的元素。

礙於時間不早,加上趕著早起投票。

本篇僅教學到 useState、useContext、createContext 應用。

為了不讓大家覺得筆者本篇... 過於敷衍過於廢文

最後提供一葛整合今日所學的奇淫技巧。

你知道嗎?奇淫技巧不一定代表是好的作法。

4. useState+useContext+createContext

前面學到的 useContext,依照本篇的內容,目前僅能讀取,卻不能改變。

大多的使用方式為: useContext + useReducer

有興趣的人,建議可以先利用「神O海O」詢問看看有沒有文章可以看。

1578677584_58859.jpg

接著,來為大家介紹這種邪門歪道,殘害新人的範例應用,

參照剛剛的範例,我們繼續修改:

首先在上面 import 我們要使用的鉤鉤,

接著將 createContext 初始值改為 null, 因為等等要改在 component 內初始化。

import React, { createContext, useContext, useState, Fragment } from 'react';

const UserContext = createContext(null);

接著我們利用 useState,建立一葛 name 的 state,

並使用 Provider 將初始值設定為陣列 [name, setName]。

function App() {
  const [name, setName] = useState('Robby');

  return (
    <UserContext.Provider value={[name, setName]} >
      <User />
    </UserContext.Provider>
  );
}

你知道嗎?為了方便管理,可以使用物件,將 [name, setName] 用物件包起來,讀取時就會是物件!

最後在子組件同樣使用 useContext 提取出來。

就可以進行讀取,以及使用 setName 修改囉~

function User() {
  const [name, setName] = useContext(UserContext);

  return (
    <Fragment>
      {name}
      <br />
      <button onClick={() => setName('Jack')}>ChangeName</button>
    </Fragment>
  );
}

完整程式碼如下:

import React, { createContext, useContext, useState, Fragment } from 'react';

const UserContext = createContext(null);

function App() {
  const [name, setName] = useState('Robby');

  return (
    <UserContext.Provider value={[name, setName]} >
      <User />
    </UserContext.Provider>
  );
}

function User() {
  const [name, setName] = useContext(UserContext);

  return (
    <Fragment>
      {name}
      <br />
      <button onClick={() => setName('Jack')}>ChangeName</button>
    </Fragment>
  );
}

export default App;

雖然上述做法可以達到 Context 的修改,不過很少這樣使用。

你知道嗎?筆者沒有特別這樣使用過... 是不是很 GY。

因為基於 useState 所產生的值,應由所屬的 component 控管。

因此正規來說,還是會以 useContext + useReducer 的方式進行管理。

-

最後

什麼是勾肥大戰?你不知道!?太弱惹喇!

916edd5e8389184a0131477e7511602c.JPG

那麼就先這樣,投票去,ㄅㄅ!

有勘誤之處,不吝指教。ob'_'ov

3 views