Context 是用來處理 App 中指定元件樹的公用資訊。
一般來說,資料是由父元件透過子元件定義的Property傳給子元件。
<ChildComponent data={data}/>
針對某些很多元件都會使用到的資料,使用這種每次用都要傳的傳遞方式很麻煩。
或是定義資料的元件跟實際用到資料的元件差了很多層,
那麼夾在兩者中間的元件就會多了很多次不必要的傳遞。
const App: React.FC = props => {
const [fontColor, setFontColor] = useState('#000000');
return <GreatGrandParent fontColor={fontColor}/>
}
const GreatGrandParent: React.FC = props => <GrandParent fontColor={props.fontColor}/>
const GrandParent: React.FC = props => <Parent fontColor={props.fontColor}/>
const Parent: React.FC = props => <>
<div style={{ color: props.fontColor }}>Hello</div>
<Child fontColor={props.fontColor}/>
</>
const Child: React.FC = props => <GrandChild fontColor={props.fontColor}/>
const GrandChild: React.FC = props => <div style={{color: props.fontColor}}>World</div>
這時就是使用 Context 的時候了!
使用 React.createContext 建立一個 Context
const defaultValue= {
fontColor: '#000000'
};
// 當元件樹外層找不到該 Context 的 Provider 時,
// Context 的值會是 defaultValue 。
const ThemeContext = React.createContext(defaultValue);
const App: React.FC = props => (
<ThemeContext.Provider value={{
fontColor: '#FF0000'
}}>
<GreatGrandParent/>
</ThemeContext.Provider>
)
[注意]
const App: React.FC = props => (
<ThemeContext.Provider value={{
fontColor: '#FF0000'
}}>
<ThemeContext.Provider value={{
fontColor: '#00FF00'
}}>
{ /* GreatGrandParent 得到的 fontColor 是 #00FF00 */ }
<GreatGrandParent/>
</ThemeContext.Provider>
</ThemeContext.Provider>
)
class GreatGrandParent extends React.Component {
componentDidMount() {
let value = this.context;
// ...
}
componentDidUpdate() {
let value = this.context;
// ...
}
componentWillUnmount() {
let value = this.context;
// ...
}
render() {
let value = this.context;
// ...
}
}
GreatGrandParent.contextType = ThemeContext;
class App extends React.Component {
render() {
return (
<ThemeContext.Provider value={{
fontColor: '#FF0000'
}}>
<UserContext.Provider value={{
name: 'world'
}}>
<ConsumerWrapper/>
</UserContext.Provider>
</ThemeContext.Provider>
);
}
}
class ConsumerWrapper extends React.Component {
render() {
return (
<ThemeContext.Consumer>
{ theme => (
<UserContext.Consumer>
{ user => (
<GreatGrandParent
color={theme.fontColor}
user={user.name}/>
)}
</UserContext.Consumer>
)}
</ThemeContext.Consumer>
);
}
}
class GreatGrandParent extends React.Component {
render() {
return <div style={{ color: this.props.color }}>
Hello, { this.props.user }
</div>
}
}
使用 React 內建 Hook: useContext
const App: React.FC = props => {
return (
<ThemeContext.Provider value={{
fontColor: '#FF0000'
}}>
<UserContext.Provider value={{
name: 'world'
}}>
<GreatGrandParent/>
</UserContext.Provider>
</ThemeContext.Provider>
);
}
const GreatGrandParent: React.FC = props => {
const { fontColor } = useContext(ThemeContext);
const { name } = useContext(UserContext);
return (<div style={{ color: fontColor }}>
Hello, { name }
</div>
);
}
When you subscribe to the blog, we will send you an e-mail when there are new updates on the site so you wouldn't miss them.
評論 3
最近讀了 Kent Dodds 的 How to use React Context effectively ,好奇您在使用 React context 時,是不是也有一套自己的組織方法?
我在使用的時候,也會客製 Provider Component!
不過我覺得使用 useContext 蠻方便的,就沒有特別去客製 Consumer ~
也可以在最外層用一個 Component 去動態包整個專案都會用到的 Provider 們 (比如說主題、工具之類的)。
不知道這樣有沒有回答到你的問題 XD
有!我也還在練習使用 React context ,謝謝你的回答~