在React如果要在組件之間傳遞資料可以使用props,用起來就像是呼叫帶入參數的函式。
function Content({text}) { // 使用解構將物件裡的text取出來
return <p>{ text } </p>
}
function App() {
return <Content text={'你好'} />
}
這種方式較適用於層級距離沒有太遠的組件,如果要傳遞資料的兩個組件之間距離很遠,中間要經過很多組件,這些組件又用不到這些資料,就可以考慮使用useContext來代替一層一層又一層層往下傳的props,包裹在最外層就可以讓底下的樹狀結構都可以使用context的值。
使用createContext建立一個context並且export出去。
import { createContext } from "react";
export const TextContext = createContext('預設值');
將context包裹在外層,並帶入value,在這之下的子層都可以讀取這個value。
import { TextContext } from "./context/textContent";
function App() {
return (
<TextContext.Provider value={"Hello"}>
<Content />
</TextContext.Provider>
);
}
export default App;
在要讀取的組件使用useContext,並帶入createContext的context
import { useContext } from "react";
import { TextContext } from "./context/textContent";
function ChildContent() {
const text = useContext(TextContext);
return <p>{text}</p>
}
export default ChildContent;
也可以這樣寫,把context的provider寫成一個組件方便使用,也把狀態都隔離出來放在這個context的組件裡面。
import { createContext } from "react";
export const TextContext = createContext();
export default function TextContextProvider({children}) {
return (
<TextContext.Provider value={'Hello'}>
{children}
</TextContext.Provider>
)
}
如果有多個context包裹著,讀到的值會是最靠近的context。以下的範例會建立一個context,然後重複使用並且一層一層包裹下去。如下圖,會有3個context然後分別帶入不同value,讓底下的Level組件讀取context的value決定字體大小。
首先,還是建立一個context
import { createContext } from "react";
export const LevelContext = createContext();
建立一個Section組件,組件裡使用剛才建立的context並且包裹著children和Level組件
import { LevelContext } from "./levelContext";
import Level from "./Level";
function Section({children ,level}) {
return <LevelContext.Provider value={level}>
<Level />
{children}
</LevelContext.Provider>
}
export default Section;
Level組件使用剛才建立的LevelContext
import { useContext } from "react";
import { LevelContext } from "./levelContext";
function Level() {
const level = useContext(LevelContext);
const content = '哈囉';
if (level === 1) {
return <h1>{ content }</h1>
}
if (level === 2) {
return <h2>{ content }</h2>
}
return <h3>{ content }</h3>
}
export default Level;
在Section組件帶入不同的level
import Section from "./context/section";
function App() {
return (
<Section level={1}>
<Section level={2}>
<Section level={5} />
</Section>
</Section>
)
}
結果如下圖,context的value會讀取到最接近。
context的value不只可以用字串或是數字,也可以把useState或是useReducer當作value將setter function或是dispatch供其他的組件共用。
在createContext後代入型別,因為default value可能會是null所以這邊給他或null的type。
type LevelValue = number | null
export const LevelContext = createContext<LevelValue>(null);
https://react.dev/learn/passing-data-deeply-with-context
https://react.dev/reference/react/useContext
https://react.dev/learn/typescript