前面章節我們有初步認識到了 React 元件傳遞過程也就是 Props,但 Props 通常是上層一路傳遞到下層,雖然這樣子可以確保資料流向,因為是單向的,但卻也會延生一些麻煩問題發生,也就是傳遞太深,因此這時候我們就要來認識一個新的東西 useContext。
就一般開發來講,我們大多都會使用 Props 將資料往下傳遞,所以讓我們用一張圖快速回顧一下前面章節 Props 的傳遞過程
我們可以從上方圖片發現 Props 必須傳遞三次才能夠將資料傳給 Button,這樣子在開發與維護上都會發生一定程度的困擾,例如:我們不確定 List
元件是不是根本不用接收 Props,但為了將資料傳遞給 Button
被迫要去接收 Props 等問題發生,因此我們在實務開發上其實很常會需要使用到跨元件溝通的。
那麼這邊就讓我們看一個實際的範例程式碼好了,或許這樣子會更有感覺?
底下是一個範例程式碼的舉例
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
需要使用到 title
與 price
的關係,所以我們被迫要整包資料往下傳遞才能給 Button
使用,這樣會讓我們不經意的傳遞了非常多資料,而導致資料流會越來越龐大且複雜,那麼這時候我們就會需要一個東西,也就是跨元件溝通的技巧,也是我們這一章節要介紹的東西 useContext
。
要使用 useContext
之前我們必須先認識一個東西也就是 createContext
,在實作跨元件溝通時,我們一開始會需要使用 createContext
來建立一個 Context Object,因為 useContext
只能接受 Context Object 才能夠正常運作
const DataContext = React.createContext();
這邊請切記 useContext
只能接受 Context Object 這件事情
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
的部分
// ...略
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
來接收資料
// ...略
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
來接收並取代。
接著讓我們看一張圖了解一下目前資料是怎麼傳遞的
透過上方圖片我們可以看到資料直接跨過了 Card
傳遞到 Button
元件。
而這個概念基本上與 Vue 的 Provide / Inject 非常雷同,你可以隨時自己決定何時要 Inject,在後面我們會來進入一些簡單的開發,讓前面知識點可以貫穿起來。
這邊有一個小小注意事項要注意,使用 Provider
的時候它能接收的只有 value
,若你改成 data={ data }
反而是無法傳遞的唷。
本文將會同步更新到我的部落格