終究都要學 React 何不現在學呢? - React 基礎 - useContext - (9)

前言

前面章節我們有初步認識到了 React 元件傳遞過程也就是 Props,但 Props 通常是上層一路傳遞到下層,雖然這樣子可以確保資料流向,因為是單向的,但卻也會延生一些麻煩問題發生,也就是傳遞太深,因此這時候我們就要來認識一個新的東西 useContext。

Props 資料傳遞

就一般開發來講,我們大多都會使用 Props 將資料往下傳遞,所以讓我們用一張圖快速回顧一下前面章節 Props 的傳遞過程

props

我們可以從上方圖片發現 Props 必須傳遞三次才能夠將資料傳給 Button,這樣子在開發與維護上都會發生一定程度的困擾,例如:我們不確定 List 元件是不是根本不用接收 Props,但為了將資料傳遞給 Button 被迫要去接收 Props 等問題發生,因此我們在實務開發上其實很常會需要使用到跨元件溝通的。

那麼這邊就讓我們看一個實際的範例程式碼好了,或許這樣子會更有感覺?

底下是一個範例程式碼的舉例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const Button = ({ data }) => {
const pay = () => {
window.alert(`你已成功購買 ${data.title}`);
}

return (
<button type="button" onClick={ pay }>點我購買({ data.price } $)</button>
)
}

const Card = ({ data }) => {
return (
<div>
<h5>產品名稱:{ data.title }</h5>
<Button data={ data }/>
</div>
)
}


const Products = () => {
const [ data, setData ] = React.useState({
title: 'PlayStation5',
price: 75000,
});

return (
<div>
<ul>
<li key={ data.title }>
<Card data={ data } />
</li>
</ul>
</div>
)
}

const App = () => {
return (
<div>
<Products />
</div>
)
}

const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);

我們可以看到範例中 Card 只會使用到 title,但因為 Button 需要使用到 titleprice 的關係,所以我們被迫要整包資料往下傳遞才能給 Button 使用,這樣會讓我們不經意的傳遞了非常多資料,而導致資料流會越來越龐大且複雜,那麼這時候我們就會需要一個東西,也就是跨元件溝通的技巧,也是我們這一章節要介紹的東西 useContext

useContext

要使用 useContext 之前我們必須先認識一個東西也就是 createContext,在實作跨元件溝通時,我們一開始會需要使用 createContext 來建立一個 Context Object,因為 useContext 只能接受 Context Object 才能夠正常運作

1
const DataContext = React.createContext();

這邊請切記 useContext 只能接受 Context Object 這件事情

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const DataContext = React.createContext();

const App = () => {
// ✅ Good!符合 useContext 用法
const data = useContext(DataContext);

// ❌ Error!不符合 useContext 用法
const data = useContext('DataContext');

// ❌ Error!不符合 useContext 用法
const data = useContext();

// ❌ Error!不符合 useContext 用法
const data = useContext(DataContext.Provider);
return (
<div>
</div>
)
}

const app = document.querySelector('#app');
const root = ReactDOM.createRoot(app);
root.render(<App />);

那麼使用 createContext 建立之後主要有兩個核心東西會使用

  • Provider
    • 傳遞資料用,會搭配一個 value 屬性。
  • Consumer
    • 接收資料用,會搭配一個 value 屬性,但這一個實際上會被 useContext 取代。

接下來該怎麼使用呢?首先 createContext 本身也是 React 元件一種,因此只需要在元件外層使用 DataContext 包覆並且加上預計要跨元件傳遞的值,並稍微調整一下 Card 的部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// ...略
const Card = ({ data }) => {
return (
<div>
<h5>產品名稱:{ data.title }</h5>
<Button/>
</div>
)
}

const Products = () => {
const [ data, setData ] = React.useState({
title: 'PlayStation5',
price: 75000,
});

return (
<DataContext.Provider value={ data }>
<ul>
<li key={ data.title }>
<Card data={ data } />
</li>
</ul>
</DataContext.Provider>
)
}
// ...略

那麼 value 概念也類似於 props

接著要改寫 Button 並改成使用 useContext 來接收資料

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ...略

const Button = () => {
const data = React.useContent(DataContext);

const pay = () => {
window.alert(`你已成功購買 ${data.title}`);
}

return (
<button type="button" onClick={ pay }>點我購買({ data.price } $)</button>
)
}

// ...略

這樣子就可以做跨元件傳遞了,你可以發現 Button 不再透過 Props 方式接收而是透過 useContext 來接收並取代。

接著讓我們看一張圖了解一下目前資料是怎麼傳遞的

value

透過上方圖片我們可以看到資料直接跨過了 Card 傳遞到 Button 元件。

而這個概念基本上與 Vue 的 Provide / Inject 非常雷同,你可以隨時自己決定何時要 Inject,在後面我們會來進入一些簡單的開發,讓前面知識點可以貫穿起來。

這邊有一個小小注意事項要注意,使用 Provider 的時候它能接收的只有 value,若你改成 data={ data } 反而是無法傳遞的唷。

後記

本文將會同步更新到我的部落格