本文介紹一下 React 中常見的 Context API 的使用方式。在使用 Context API 之前,我們還需要知道為啥要使用。❓
為啥要用 Context API
考慮到元件有可能 層層巢狀 ,在傳 props 的過程中,如果書寫大量的 ...props
或 propName={this.props.propValue}
會導致程式碼灰常醜陋 ?:
一層一層下來就像這樣:
<App>
<Switcher toggleState={this.state.toggle}>
<Pannel toggleState={props.toggleState}>
<div onClick={handleClick}>{props.toggleState ? '✔' : '❌'}
複製程式碼
所以引入 Context API 就可以直接通過上下文跨層級獲取資料:
如何使用
- 然後建立 provider ?
- 首先要引入 React 內建的 React Context API ?
- 最後建立 consumer ?
建立 Provider
增加一個名為 ToggleContext.js
的檔案作為上下文?,裡頭定義一系列需要跨層級使用的 state 和 function
import React, { createContext } from 'react'
// 1. 使用 createContext 建立上下文
const ToggleContext = createContext({
toggle: true,
handleToggle: () => {}
})
// 2. 建立 Provider
export class ToggleProvider extends React.Component {
// 注意書寫順序;handleToggle 作為箭頭函式不能 bind 因此需要寫在上面;如果不喜歡這樣的順序則可以書寫普通函式放在下面但記得 bind
handleToggle = () => {
this.setState({ toggle: !this.state.toggle })
}
// 2-1. 重寫 state
state = {
toggle: true,
handleToggle: this.handleToggle
}
render() {
// 2-2. 通過 Provider 元件的 value 將 state 提供出去
return (
<ToggleContext.Provider value={this.state}>
{this.props.children}
</ToggleContext.Provider>
)
}
}
// 3. 建立 Consumer
export const ToggleConsumer = ToggleContext.Consumer
複製程式碼
上面的程式碼主要分為三大部分:
// 建立 Context
const ToggleContext = createContext()
// 建立 Provider
export class ToggleProvider extends React.Component {}
// 建立 Consumer
export cnost ToggleConsumer = ToggleContext.Consumer
複製程式碼
我們理一下步驟?
- 首先,我們需要引入
createContext
上下文並呼叫,傳入我們希望在其他層級元件中使用的state
和改變state
的方法,注意這裡的state
和方法只是一個“骨架”,後面的Provider
會覆蓋 - 接下來建立
Provider
這裡頭維護真正的state
,並通過render
函式裡面的Context.Provider
元件的value
屬性提供這些方法 - 然後建立
Consumer
,直接匯出Context.Consumer
給外部使用即可
使用 Provider
ToggleProvider
元件包裝了一系列共享的狀態,為了使用這些元件的狀態,我們直接將其新增到 App 元件中:
import React from 'react';
import { ToggleProvider } from './ToggleContext' // 1. 獲取 Provider
function App() {
// 2-1. 使用 ToggleProvider 元件
// 2-2. 如果有其他元件一樣可以共享 state
return (
<ToggleProvider>
<Switcher></Switcher>
{/* 其他元件仍然可以通過 props 訪問到共享的 state */}
</ToggleProvider>
);
}
// ...
export default App;
複製程式碼
使用 Provider 比較簡單直接作為父元件包裹在上層即可。如果元件內部有其他多個元件,這些元件都可以共享 Provider 提供的 state
使用 Consumer
- 通過 Consumer 直接使用 props 傳遞的 state 屬性在 render 函式中渲染即可
- 如果需要呼叫方法,則可呼叫 props 傳遞的函式
import React from 'react';
import { ToggleProvider, ToggleConsumer } from './ToggleContext' // 1. 獲取 Provider 和 Consumer
function App() {
return (
<ToggleProvider>
<Switcher></Switcher>
</ToggleProvider>
);
}
const Switcher = () => {
return <Pannel />
}
const Pannel = () => {
// 在多個層級內直接通過 props 獲取 state 和方法,呼叫方法改變 state
return (
<ToggleConsumer>
{({ toggle, handleToggle }) => <div onClick={() => handleToggle()}>{ toggle ? '✔' : '❌'}</div>}
</ToggleConsumer>
)
}
export default App;
複製程式碼
直接在子元件內部通過 props 呼叫即可
小結
另外附上一個簡易版的 Context:
- 通過 createContext 建立一個名為 color 的 context
- 通過 Provider 的 value 屬性傳值
- 通過 Consumer 的 props 接收值
import React, { createContext } from "react";
const { Provider, Consumer } = createContext("color"); // 建立 Context 引用 Provider 和 Consumer
class DeliverComponent extends React.Component {
// 維護一個 state
state = {
color: 'orange',
handleClick: () => {
this.setState({ color: 'red' })
}
}
render() {
return (
<Provider value={this.state}>
<MidComponent />
</Provider>
)
}
}
const MidComponent = () => <Receiver />; // 中間包含多層級的元件
const Receiver = () => ( // 需要使用的後代元素使用 Consumer
<Consumer>
{({ color, handleClick }) => <div style={{ color }} onClick={() => { handleClick() }}> Hello, this is receiver.</div>}
</Consumer>
);
const App = () => <DeliverComponent />;
export default App;
複製程式碼
參考:
- https://blog.usejournal.com/sharing-state-using-reacts-context-api-bc2db94da46d