Hooks
出了有段時間了,不知盆友們有在專案中開始使用了嗎❓如果還沒了解的童鞋,可以瞧瞧這篇文章,對比看下三大基礎 Hooks
和傳統 class
元件的區別和用法吧?
我們所指的三個基礎 Hooks
是:
useState
在函式式元件內維護state
useEffect
函式式元件內有副作用的呼叫與componentDidMount
、componentDidUpdate
類似但又有所區別useContext
監聽 provider 更新變化
useState
useState
允許我們在函式式元件中維護 state
,傳統的做法需要使用類元件。舉個例子?,我們需要一個輸入框,隨著輸入框內容的改變,元件內部的 label
標籤顯示的內容也同時改變。下面是兩種不同的寫法:
不使用 useState:
import React from "react";
// 1
export class ClassTest extends React.Component {
// 2
state = {
username: this.props.initialState
}
// 3
changeUserName(val) {
this.setState({ username: val })
}
// 4
render() {
return (
<div>
<label style={{ display: 'block' }} htmlFor="username">username: {this.state.username}</label>
<input type="text" name="username" onChange={e => this.changeUserName(e.target.value)} />
</div>
)
}
}
複製程式碼
- 我們需要定義一個類並從
React.Component
處繼承 - 還需要初始化一個
state
- 初始化改變
state
的方法 - 最後還要使用
render
函式返回JSX
✅使用 useState:
// 1
import React, { useState } from "react";
export function UseStateTest({ initialState }) {
// 2
let [username, changeUserName] = useState(initialState)
// 3
return (
<div>
<label style={{ display: 'block' }} htmlFor="username">username: {username}</label>
<input type="text" name="username" onChange={e => changeUserName(e.target.value)} />
</div>
)
}
複製程式碼
在父元件中使用:
import React from "react";
// 引入元件
import { UseStateTest } from './components/UseStateTest'
// 4
const App = () => (
<div>
<UseStateTest initialState={'initial value'} />
</div>
)
export default App;
複製程式碼
- 需要從
react
中引入useState
這個? - 使用
useState
方法,接收一個初始化引數,定義state
變數,以及改變state
的方法 - 在需要使用的地方直接使用
state
這個變數即可,增加事件處理函式觸發改變state
的方法 - 在父元件中呼叫,通過
props
傳遞initialState
初始化值
用 useState
方法替換掉原有的 class
不僅效能會有所提升,而且可以看到程式碼量減少很多,並且不再需要使用 this
,所以能夠維護 state
的函式式元件真的很好用?
class classTest extends React.Components {}
?function UseStateTest(props) {}
函式式元件寫法this.state.username
?username
使用state
不需要this
this.setState({ username: '' })
?changeUserName('')
改變state
也不需要書寫setState
方法
文件說明:https://zh-hans.reactjs.org/docs/hooks-state.html
useEffect
useEffect
是專門用來處理副作用的,獲取資料、建立訂閱、手動更改 DOM 等這都是副作用。你可以想象它是 componentDidMount
和 componentDidUpdate
及 componentWillUnmount
的結合。
舉個例子??,比方說我們建立一個 div
標籤,每當點選就會傳送 http
請求並將頁面 title
改為對應的數值:
import React from 'react'
// 1
import { useState, useEffect } from 'react'
export function UseEffectTest() {
let [msg, changeMsg] = useState('loading...')
// 2
async function getData(url) { // 獲取 json 資料
return await fetch(url).then(d => d.json())
}
// 3
async function handleClick() { // 點選事件改變 state
let data = await getData('https://httpbin.org/uuid').then(d => d.uuid)
changeMsg(data)
}
// 4
useEffect(() => { // 副作用
document.title = msg
})
return (
<div onClick={() => handleClick()}>{msg}</div>
)
}
複製程式碼
- 首先從
react
中引入useEffect
? - 然後建立獲取資料的
getData
方法 - 建立事件處理函式
handleClick
- 使用
useEffect
處理副作用:改變頁面的title
如果使用傳統的類元件的寫法:
import React from 'react'
// 1
export class ClassTest extends React.Component {
// 2
state = {
msg: 'loading...'
}
// 3
async getData(url) { // 獲取 json 資料
return await fetch(url).then(d => d.json())
}
handleClick = async () => { // 點選事件改變 state
let data = await this.getData('https://httpbin.org/uuid').then(d => d.uuid)
this.setState({ msg: data })
}
// 4
componentDidMount() {
document.title = this.state.msg
}
componentDidUpdate() {
document.title = this.state.msg
}
// 5
render() {
return (
<div onClick={this.handleClick}>{this.state.msg}</div>
)
}
}
複製程式碼
- 首先建立類
ClassTest
- 初始化
state
- 定義獲取資料方法和事件處理函式
- 在
componentDidMount
和componentDidUpdate
階段改變document.title
- 最後通過
render
函式渲染
這一堆東西寫完人都睡著了?
使用 useEffect 不僅去掉了部分不必要的東西,而且合併了 componentDidMount
和 componentDidUpdate
方法,其中的程式碼只需要寫一遍。?
第一次渲染和每次更新之後都會觸發這個鉤子,如果需要手動修改自定義觸發規則
見文件:https://zh-hans.reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
另外,官網還給了一個訂閱清除訂閱的例子:
使用 useEffect
直接 return
一個函式即可:
返回的函式是選填的,可以使用也可以不使用:
文件:https://zh-hans.reactjs.org/docs/hooks-effect.html#recap
比方說我們使用 useEffect 來解綁事件處理函式:
useEffect(() => {
window.addEventListener('keydown', handleKeydown);
return () => {
window.removeEventListener('keydown', handleKeydown);
}
})
複製程式碼
useContext
useContext
的最大的改變是可以在使用 Consumer
的時候不必在包裹 Children
了,比方說我們先建立一個上下文,這個上下文裡頭有一個名為 username
的 state
,以及一個修改 username
的方法 handleChangeUsername
建立上下文
不使用 useState:
不使用 state hooks
的程式碼如下:
import React, { createContext } from 'react'
// 1. 使用 createContext 建立上下文
export const UserContext = new createContext()
// 2. 建立 Provider
export class UserProvider extends React.Component {
handleChangeUsername = (val) => {
this.setState({ username: val })
}
state = {
username: '',
handleChangeUsername: this.handleChangeUsername
}
render() {
return (
<UserContext.Provider value={this.state}>
{this.props.children}
</UserContext.Provider>
)
}
}
// 3. 建立 Consumer
export const UserConsumer = UserContext.Consumer
複製程式碼
看看我們做了什麼:
- 首先我們使用
createContext
建立了上下文 - 然後我們建立
Provider
;裡頭定義了handleChangeUsername
方法和username
的state
,並返回一個包裹this.props.children
的Provider
- 最後我們再返回
UserContext.Consumer
程式碼比較冗長,可以使用上文提到的 useState
對其進行精簡:
✅使用 useState:
使用 state hooks
:
import React, { createContext, useState } from 'react'
// 1. 使用 createContext 建立上下文
export const UserContext = new createContext()
// 2. 建立 Provider
export const UserProvider = props => {
let [username, handleChangeUsername] = useState('')
return (
<UserContext.Provider value={{username, handleChangeUsername}}>
{props.children}
</UserContext.Provider>
)
}
// 3. 建立 Consumer
export const UserConsumer = UserContext.Consumer
複製程式碼
使用 useState
建立上下文更加簡練。
使用上下文
上下文定義完畢後,我們再來看使用 useContext
和不使用 useContext
的區別是啥?:
不使用 useContext:
import React from "react";
import { UserConsumer, UserProvider } from './UserContext'
const Pannel = () => (
<UserConsumer>
{/* 不使用 useContext 需要呼叫 Consumer 包裹 children */}
{({ username, handleChangeUsername }) => (
<div>
<div>user: {username}</div>
<input onChange={e => handleChangeUsername(e.target.value)} />
</div>
)}
</UserConsumer>
)
const Form = () => <Pannel></Pannel>
const App = () => (
<div>
<UserProvider>
<Form></Form>
</UserProvider>
</div>
)
export default App;
複製程式碼
✅使用 useContext:
只需要引入 UserContext
,使用 useContext
方法即可:
import React, { useContext } from "react"; // 1
import { UserProvider, UserContext } from './UserContext' // 2
const Pannel = () => {
const { username, handleChangeUsername } = useContext(UserContext) // 3
return (
<div>
<div>user: {username}</div>
<input onChange={e => handleChangeUsername(e.target.value)} />
</div>
)
}
const Form = () => <Pannel></Pannel>
// 4
const App = () => (
<div>
<UserProvider>
<Form></Form>
</UserProvider>
</div>
)
export default App;
複製程式碼
看看做了啥:
- 首先第一步引入
useContext
- 然後引入
UserProvider
以及上下文UserContext
- 再需要使用的元件中呼叫
useContext
方法獲取state
- 當然前提是要在父元件中使用
UserProvider
巢狀主children
這樣通過 useContext
和 useState
就重構完畢了,看起來程式碼又少了不少?
以上,三個基礎的 Hooks
入門就講解完畢了,上手就是這樣,函式式元件和 Hooks
配合使用真的非常爽⛄
參考:
- https://codeburst.io/quick-intro-to-react-hooks-6dd8ecb898ed
- https://reactjs.org/docs/hooks-reference.html