函數類組件
函數式組件和類式組件的區別:
React組件可以分為類組件和函數式組件,兩者最明顯的不同就是在語法上,函數組件是一個純函數,它接收一個props對象返回一個react元素。而類組件需要去繼承React.Component並且創建render函數返回react元素,這將會要更多的代碼,雖然它們實現的效果相同。
- 函數式組件中沒有state
- 函數式組件中沒有生命周期(重點,涉及到生命周期的方法只能在類組件中定義。)
在較新的react版本看中添加了hooks,使得我們可以在函數組件中使用useState鈎子去管理state,使用useEffect鈎子去使用生命周期函數。從這個改版中我們可以看出作者更加看重函數組件,而且react團隊曾提及到在react之后的版本將會對函數組件的性能方面進行提升。
為什么要使用函數式組件?
- Hooks是比高階組件(HOC)和render props更優雅的邏輯復用方式。這個是很多人喊“真香”的原因。優雅的邏輯復用方式,會促進一個更加蓬勃的生態,這對於原本生態就很強的React來說是如虎添翼。會有更多的人願意把自己的邏輯抽離成hooks(因為真的太優雅了),發布為library,為react生態添磚加瓦(看看這段時間各種基於hooks的狀態管理工具)。我還認為,很快會出現一些“hooks生態圈”的“lodash”。
- 函數式組件的心智模型更加“聲明式”。hooks(主要是useEffect)取代了生命周期的概念(減少API),讓開發者的代碼更加“聲明化”:
- 舊的思維:“我在這個生命周期要檢查props.A和state.B(props和state),如果改變的話就觸發xxx副作用”。這種思維在后續修改邏輯的時候很容易漏掉檢查項,造成bug。
- 新的思維:“我的組件有xxx這個副作用,這個副作用依賴的數據是props.A和state.B”。從過去的命令式轉變成了聲明式編程。
- 其實仔細想一想,人們過去使用生命周期不就是為了判斷執行副作用的時機嗎?現在hooks直接給你一個聲明副作用的API,使得生命周期變成了一個“底層概念”,無需開發者考慮。開發者工作在更高的抽象層次上了。
- 類似的道理,除了聲明副作用的API,react還提供了聲明“密集計算”的API(useMemo),取代了過去“在生命周期做dirty檢查,將計算結果緩存在state里”的做法。React內核幫你維護緩存,你只需要聲明數據的計算邏輯以及數據的依賴。
- 函數式組件的心智模型更加“函數式”。react團隊正在循序漸進地教育社區,為未來的並發模式打下基礎。(其實react從一開始就受到了很多函數式編程的影響,現在推行函數式組件算是“回歸初心”)。下面我會詳細討論函數式組件的心智模型。
Hooks概念及常用的Hooks
Hook 是一些可以讓你在函數組件里“鈎入” React state 及生命周期等特性的函數。Hook 不能在 class 組件中使用 —— 這使得你不使用 class 也能使用 React。
React官網是這樣描述Hooks的:Hook 是 React 16.8 的新增特性。它可以讓你在不編寫 class 的情況下使用 state 以及其他的 React 特性。Hook 將組件中相互關聯的部分拆分成更小的函數(比如設置訂閱或請求數據),而並非強制按照生命周期划分。你還可以使用 reducer 來管理組件的內部狀態,使其更加可預測。
1. useState
: State的Hook
State Hook讓函數組件也可以有state狀態, 並進行狀態數據的讀寫操作。
語法
const [xxx, setXxx] = React.useState(initValue)
useState()說明:
參數: 第一次初始化指定的值在內部作緩存;
返回值: 包含2個元素的數組, 第1個為內部當前狀態值, 第2個為更新狀態值的函數。
可以多次使用useState()
。
setXxx()2種寫法:
setXxx(newValue)
: 參數為非函數值, 直接指定新的狀態值, 內部用其覆蓋原來的狀態值
setXxx(value => newValue)
: 參數為函數, 接收原本的狀態值, 返回新的狀態值, 內部用其覆蓋原來的狀態值
示例
import React, { useState } from 'react';
function Example() {
// 聲明一個叫 “count” 的 state 變量。
const [count, setCount] = useState(0);
// 加的回調 setXxx()第二種寫法
function add() {
setCount(count => count + 1);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me to + 1
</button>
<button onClick={() => setCount(add)}>
Click me to add 1
</button>
</div>
);
}
export default Example
2. useEffect
: 副作用的Hook
Effect Hook 可以讓你在函數組件中執行副作用操作(用於模擬類組件中的生命周期鈎子)。
我們之前可能已經在React組件中執行過數據獲取、訂閱或者手動修改過DOM。我們統一把這些操作稱為“副作用”,或者簡稱為“作用”。它跟 class 組件中的 componentDidMount
、componentDidUpdate
和 componentWillUnmount
具有相同的用途,只不過被合並成了一個 API。
React中的副作用操作
- 發ajax請求數據獲取
- 設置訂閱 / 啟動定時器
- 手動更改真實DOM
語法和說明:
- 通過使用這個 Hook,你可以告訴 React 組件需要在渲染后執行某些操作
- 將
useEffect
放在組件內部讓我們可以在 effect 中直接訪問count
state 變量(或其他 props)。 - 默認情況下,它在第一次渲染之后和每次更新之后都會執行,即不加第二個參數的情況下。
總體說明:
useEffect(() => {
// 在此可以執行任何帶副作用操作
return () => { // 在組件卸載前執行
// 在此做一些收尾工作, 比如清除定時器/取消訂閱等
}
}, [stateValue]) // 如果指定的是[], 回調函數只會在第一次render()后執行
可以多次使用useEffect
。
替代componentDidMount
:
useEffect(() => {
// 在此可以執行任何帶副作用操作,來初始化代碼
}, [stateValue]) // 如果指定的是[], 回調函數只會在第一次render()后執行
替代componentDidUpdate
:
useEffect(() => {
// 在此可以執行任何帶副作用操作
}, [stateValue]) // 如果指定的是[], 回調函數只會在第一次render()后執行,此時不能為空,數組內的值為所監控的state的值,如果不加此參數,默認為所有的state的值
// 例如如下代碼
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 僅在 count 更改時更新
替代componentWillUnmount
:
useEffect(() => {
return () => { // 在組件卸載前執行
// 在此做一些收尾工作, 比如清除定時器/取消訂閱等
}
})
實例:
import React, { useState, useEffect } from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
3. useRef
: ref的Hook
Ref Hook可以在函數組件中存儲/查找組件內的標簽或任意其它數據,跟React.createRef()
類似。
useRef
返回一個可變的 ref 對象,其 .current
屬性被初始化為傳入的參數(initialValue
)。返回的 ref 對象在組件的整個生命周期內保持不變。
語法:
const refContainer = useRef()
ref 對象的 .current
屬性為相應的 DOM 節點。
實例:
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已掛載到 DOM 上的文本輸入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
函數類組件中使用props
使用方式類似類組件的props使用,父級組件需要暴露接口給子組件,子組件才能接收,子組件接收數據的方法:直接作為函數的參數(props)傳給子組件。
import React, { useState } from 'react'
import Title from "../Title"
//父級組件傳過去
function Parent() {
let [count, setCount] = useState(1)
const add = (count) => { setCount(count) }
return (<div>
//類似類組件中的接口傳遞數據到子組件
<Title count={count} />
<button onClick={() => add(count + 1)}>+</button>
</div>)
}
export default Parent;
//子組件接收
import React from 'react'
function Title(props) {
return (
<h1>{props.count}</h1>
)
}
export default Title;