都 2020 年了,還不趕快用 Hooks 參加勾肥大戰?
React 在 16.8 版之後納入 Hook 的功能。
前言
去年還在研究所打混,經過老闆再三囑咐。對於額外技術也暫時停止學習。
身為一位學術研究員,不專注於非研究領域的程式發展,也很合邏輯 QQ。
目前 CRA ( create-react-app ) 所使用的版號,也來到 16.12.0,
學習永遠不嫌遲,趁著一年國軍 online,趕緊補一下!
本篇文章僅簡單介紹、筆記,建議大家可以閱讀:
1. Hooks 介紹
React 在 2019 年 2 月 Release v16.8.0,
該版本推出了 Hooks ,鉤子 (?
可以讓我們在 functional component 進行一些 state 的控管。
儘管 Angular 已經來到 8.3 ...
儘管 Vue 已經來到 2.6 ...
但 Hooks 的名詞,勢必會影響不少生態圈(X
春秋百家爭鳴 大家抄來抄去 到底誰有道理 誰的比較高明
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;
useContext
如果我們要從父組件傳值給子子孫孫等組件,通常要 props 很多層,
而你的專案可能又包了一堆 HOC,例如:
export default withTranslation()(connect(mapStateToProps)(Todo));
嚴重時會破壞你的結構,大 guy 會變成下麵這葛樣子, ( 俗稱 Wrapper Hell ):
如今!!!
只要你會丟出鉤子,稍微彎拉一下...
就能實現並解決這葛垢病!
createContext
在使用 useContext 之前,要先認識 createContext,它是用來創建 Context 的 Provider 。
Context ???
Provider ???
以下使用常見的套件來比喻:
- react-redux 的 Provider
- styled-components 的 ThemeProvider
筆者簡單舉例父子組件傳遞值範例:
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,
- Provider:用於父層,並利用 value,來塞入指定的初始值。
- 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,
過度謹慎的你可能會想改寫...
沒錯,你也可以拉出來再寫一個 const arrow function。
function User() {
const renderName = name => {
return name;
}
return (
<UserContext.Consumer>
{renderName}
</UserContext.Consumer>
);
}
需要注意的是,不可以在 Consumer 同層額外寫其他東西。
但你可以在 Consumer 組件中寫其他元素。
過度謹慎的你可能會不相信...
return (
<UserContext.Consumer>
{renderName}
<div>123</div>
</UserContext.Consumer>
);
return (
<div>
<p>Hi, my name is</p>
<UserContext.Consumer>
{renderName}
</UserContext.Consumer>
</div>
);
-
過度謹慎的你可能會越想越不對勁...
一開始已經在 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;
所以上述範例才故意這樣寫兩次。
-
過度謹慎的你可能會想省時間...
每次都寫 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;
礙於時間不早,加上趕著早起投票。
本篇僅教學到 useState、useContext、createContext 應用。
為了不讓大家覺得筆者本篇... 過於敷衍過於廢文
最後提供一葛整合今日所學的奇淫技巧。
4. useState+useContext+createContext
前面學到的 useContext,依照本篇的內容,目前僅能讀取,卻不能改變。
大多的使用方式為: useContext + useReducer
有興趣的人,建議可以先利用「神O海O」詢問看看有沒有文章可以看。
接著,來為大家介紹這種邪門歪道,殘害新人的範例應用,
參照剛剛的範例,我們繼續修改:
首先在上面 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>
);
}
最後在子組件同樣使用 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 的修改,不過很少這樣使用。
因為基於 useState 所產生的值,應由所屬的 component 控管。
因此正規來說,還是會以 useContext + useReducer 的方式進行管理。
-
最後
什麼是勾肥大戰?你不知道!?太弱惹喇!
- Ref:【心得】勾肥入門教學文【1.25版】,
那麼就先這樣,投票去,ㄅㄅ!
有勘誤之處,不吝指教。ob'_'ov