陣列排序方法
Hook 是什麼?
Hook 是⼀個特殊的函式,它可以讓你“鉤⼊” React 的特性。例如,useState 是允許你在 React 函式元件中新增 state 的 Hook。
什麼時候我會⽤ Hook?如果你在編寫函式元件並意識到需要向其新增⼀些 state,以前的做法是必須將其它轉化為 class。現在你可以在現有的函式元件中使⽤Hook。
useState
import React, { useState } from "react";
export default function HookPage(props) {
// 宣告⼀個叫 “count” 的 state 變數,初始化為0
const [count, setCount] = useState(0);
return (
<div>
<h3>HookPage</h3>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
}
useEffect
Effect Hook 可以讓你在函式元件中執⾏副作⽤操作。 資料獲取,設定訂閱以及⼿動更改 React 元件中的 DOM都屬於副作⽤。不管你知不知道這些操作,或 是“副作⽤”這個名字,應該都在元件中使⽤過它們。
import React, { useState, useEffect } from "react";
export default function HookPage(props) {
// 宣告⼀個叫 “count” 的 state 變數,初始化為0
const [count, setCount] = useState(0);
// 與 componentDidMount 和 componentDidUpdate相似
useEffect(() => {
// 更新 title
document.title = `You clicked ${count} times` ;
});
return (
<div>
<h3>HookPage</h3>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
</div>
);
}
在函式元件主體內(這⾥指在 React 渲染階段)改變 DOM、新增訂閱、設定定時器、記錄⽇志以及執⾏其他包含副作⽤的操作都是不被允許的,因為這可能會產⽣莫名其妙的 bug 並破壞 UI 的⼀致性。
使⽤ useEffect完成副作⽤操作。賦值給 useEffect 的函式會在元件渲染到螢幕之後執⾏。你可以 把 effect 看作從 React的純函式式世界通往命令式世界的逃⽣通道。
預設情況下,effect 將在每輪渲染結束後執⾏,但你可以選擇讓它 在只有某些值改變的時候才執⾏。
- effect 的條件執⾏
預設情況下,effect 會在每輪元件渲染完成後執⾏。這樣的話,⼀旦 effect 的依賴發⽣變化,它就會被 重新建立。然⽽,在某些場景下這麼做可能會矯枉過正。⽐如,在上面的訂閱示例中,我們不需要在每次元件 更新時都建立新的訂閱,⽽是僅需要在 sourceprops 改變時重新建立。 要實現這⼀點,可以給 useEffect 傳遞第⼆個引數,它是 effect 所依賴的值陣列。
更新後的示例如下:
import React, { useState, useEffect } from "react";
export default function HookPage(props) {
// 宣告⼀個叫 “count” 的 state 變數,初始化為0
const [count, setCount] = useState(0);
const [date, setDate] = useState(new Date());
// 與 componentDidMount 和 componentDidUpdate相似
useEffect(() => {
// 更新 title
document.title = `You clicked ${count} times`;
}, [count]);
useEffect(() => {
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
}, []);
return (
<div>
<h3>HookPage</h3>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<p>{date.toLocaleTimeString()}</p>
</div>
);
}
此時,只有當 useEffect第⼆個引數陣列⾥的數值 改變後才會重新建立訂閱。
- 清除 effect
通常,元件解除安裝時需要清除 effect 建立的諸如訂閱或計時器 ID 等資源。要實現這⼀點, useEffect函式需返回⼀個清除函式,以防⽌記憶體洩漏,清除函式會在元件解除安裝前執⾏。
useEffect(() => {
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
return () => clearInterval(timer);
}, []);
useMemo
把“建立”函式和依賴項陣列作為引數傳⼊ useMemo ,它僅會在某個依賴項改變時才重新計算memoized 值。這種優化有助於避免在每次渲染時都進⾏⾼開銷的計算。
import React, { useState, useMemo } from "react";
export default function UseMemoPage(props) {
const [count, setCount] = useState(0);
const expensive = useMemo(() => {
console.log("compute");
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
//只有count變化,這⾥才重新執⾏
}, [count]);
const [value, setValue] = useState("");
return (
<div>
<h3>UseMemoPage</h3>
<p>expensive:{expensive}</p>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<input value={value} onChange={event => setValue(event.target.value)} />
</div>
);
}
useCallback
把內聯回撥函式及依賴項陣列作為引數傳⼊ useCallback ,它將返回該回調函式的 memoized 版本,該回調函式僅在某個依賴項改變時才會更新。當你把回撥函式傳遞給經過優化的並使⽤引⽤相等性去避免⾮必要渲染(例如shouldComponentUpdate )的⼦元件時,它將⾮常有⽤。
import React, { useState, useCallback, PureComponent } from "react";
export default function UseCallbackPage(props) {
const [count, setCount] = useState(0);
const addClick = useCallback(() => {
let sum = 0;
for (let i = 0; i < count; i++) {
sum += i;
}
return sum;
}, [count]);
const [value, setValue] = useState("");
return (
<div>
<h3>UseCallbackPage</h3>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<input value={value} onChange={event => setValue(event.target.value)} />
<Child addClick={addClick} />
</div>
);
}
class Child extends PureComponent {
render() {
console.log("child render");
const { addClick } = this.props;
return (
<div>
<h3>Child</h3>
<button onClick={() => console.log(addClick())}>add</button>
</div>
);
}
}
useCallback(fn, deps) 相當於 useMemo(() => fn, deps) 。
注意
依賴項陣列不會作為引數傳給“建立”函式。雖然從概念上來說它表現為:所有“建立”函式中引⽤的
值都應該出現在依賴項陣列中。未來編譯器會更加智慧,屆時⾃動建立陣列將成為可能。
⾃定義Hook
有時候我們會想要在元件之間重⽤⼀些狀態邏輯。⽬前為⽌,有兩種主流⽅案來解決這個問題:⾼階元件和 render props。⾃定義 Hook可以讓你在不增加元件的情況下達到同樣的⽬的。
⾃定義 Hook 是⼀個函式,其名稱以 “use” 開頭,函式內部可以調⽤其他的 Hook。
import React, { useState, useEffect } from "react";
export default function CustomHookPage(props) {
//定義⼀個叫count的state變數,初始化為0
const [count, setCount] = useState(0);
//和didMount、didUpdate類似
useEffect(() => {
console.log("count effect");
// 只需要在count發⽣改變的時候執⾏就可以啦
document.title = `點選了${count}次`;
}, [count]);
return (
<div>
<h3>⾃定義Hook</h3>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<p>{useClock().toLocaleTimeString()}</p>
</div>
);
}
//⾃定義hook,命名必須以use開頭
function useClock() {
const [date, setDate] = useState(new Date());
useEffect(() => {
console.log("date effect");
//只需要在didMount時候執⾏就可以了
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
//清除定時器,類似willUnmount
return () => clearInterval(timer);
}, []);
return date;
}
- Hook 使⽤規則
Hook 就是 JavaScript 函式,但是使⽤它們會有兩個額外的規則:
1.只能在函式最外層調⽤ Hook。不要在迴圈、條件判斷或者⼦函式中調⽤。
2.只能在 React 的函式元件中調⽤ Hook。不要在其他 JavaScript 函式中調⽤。(還有⼀個地⽅可以調⽤ Hook —— 就是⾃定義的 Hook 中。)