---
# System prepended metadata

title: React Context API
tags: [ReactJS]

---

###### tags: `ReactJS`

# React Context API
- 給予元件 Context 不限定於根元素，在其 Context 下的子元件皆可取得該 Store
- Redux 只能建立一個全域的 Store 

# Context API vs Redux
- 當擁有大量全域變數時，會造成 APP 非常龐大
- 解法: 多做一層元件包裹，將 State 相關的抽出去那層即可，利用 Props下傳即可
- 最底下有範例

# Notes
- 仍需要 react-redux / redux-thunk / redux 套件?

# 選擇是否使用 Context API 需要關注的點 
- [建議可以看看此段解說](http://bit.ly/2NlBvx4)
- Context 可用於更新頻率低的 State 
- 更新頻率高的 State，React 會需要往上查找而耗費多餘的效能

# Links
- [React Context API 教學 (Class Base 版)](http://bit.ly/2ClfAQA)
- [React Context API 教學 (Function Base 版)](http://bit.ly/2JZ8vsX)
- [React Context & Hooks (Net Ninja)](http://bit.ly/2NMGupx)
- [Functional Base 使用 useContext 取得 Context 內容](http://bit.ly/2ChhOAm)
- [引入 Redux Reducer 寫法(useContext & useReducer)](http://bit.ly/34zxECv)

# 範例

# 建立 context 檔案
- context/example-context.js
```javascript=
import React from 'react'

// 等同建立 Store 
// 必須 export 才可給予 元件使用，在此可以設置預設值
export default React.createContext({
  userList: [],
  // 建議在這裡補上用到那些 Function & 變數 (autocomplete才會支援)
  addUser: () => {}
})
```

# 父元件引用
```javascript=
// 父元件中引用 此 Context
// 給予名稱大駝峰 (我們要將其視為元件來使用)
import ExampleContext from '/context/example-context'

import React from 'react'

export default function parent() {

  // 亦可搬進元件撰寫預設值
  const [userList, setUserList] = useState([])

  // 設定 State
  const addUser = user => {
    setTimeout(() => {
      this.setState({ userList: [1,2,3] })
    }, 1000)
  }

  // value 為雙層 傳遞物件下去給子元件，Function 亦可傳給子元件使用
  return (
    <ExampleContext.Provider value={{
      userList,
      addUser,
    }}>
      {/* 給予 value 動態值，當數值變動，子元件會做重新渲染 */}
      {/* 包裹元件原本的內容 */}
    </ExampleContext.Provider>
  )
}
```

# 子元件使用 Context
- 法1: 使用 ExampleContext.Consumer 包裹
- 法2: 使 contextType (限定於 ClassBase & 16.6+版本)
- 法3: 利用 useContext 取代 Consumer (Function Base)
```javascript=
// 子元件中使用
import ExampleContext from '/context/example-context'

import React from 'react'

// 子元件為 Consumer
export default function child() {
  return (
    <ExampleContext.Consumer>
      {/* 原始內容 可以使用 context 參數 */}
      {context => (
        <h1>{context.test}</h1>
      )}
    </ExampleContext.Consumer>
  )
}
```

```javascript=
// 即可使用 this.context 取得 context 值
// 即不需要使用 ExampleContext.Consumer 包裹
static contextType = ExampleContext
```

# 全域 Store 層抽出
- context/GlobalState.js
```javascript=
// 將 GlobalState 層從 APP 抽出
// 記得更改 Import
import React from 'react'

export default function GlobalState(props) {

  // 將全域的 State、Function 等，抽出放在此元件中
  const [userList, setUserList] = useState([])
  const addUser = user => {}

  return (  
    <GlobalState.Provider value={{
      userList,
      addUser
    }}>
    {props.children}
    </GlobalState.Provider>
  )
}

// 回到 App.js 將其內容以 GlobalState 包裹即可
<GlobalState></GlobalState>
```

# 使用 useContext 取得 Context 內容
* Functional Base 
```javascript=
import React, {useContext} from 'react'
import ExampleContext from '/context/example-context'

export function Index () {
	const msg = useContext(ExampleContext);
	
	return (
		<div>
			<h1>{msg}</h1>
		</div>
	)	
}
```

# useReducer (待補)
- [useReducer 介紹](http://bit.ly/2NmpZSe)
- 概念與 Redux 類似，加上 Ducks (將 ActionType、Action、Reducer寫在同一支 )
- 可以將 Function 抽出成另一支檔案，export Reducer
```javascript=

```