# React Hook - useContext ###### tags: `Javascript, React` > useContext 是一個傳遞 props 的方法,可以很方便的傳遞到需求的 component 而不需要路過其他地方(方向只能父傳子) ## Class component 中的 context API 先從 class API 複雜版本的介紹起 我們要做到的效果是:點擊按鈕切換 Class Theme 的顏色 ![](https://i.imgur.com/IDIcjLI.png) ### App.js 首先為了操作 context API 1. 輸出 ThemeContext = React.createContext() 並且在要使用的 component 內接收 2. 使用 ThemeContext.Provider 包裹住要被傳遞 context 的 component 們 只要以上兩個步驟就可以把 App 內的 useState 給其他 component 使用摟! 這邊注意在 Provider 裡面不管包了幾層,都只要引入 ThemeContext 就可以使用 parent component 的 props 摟 ```javascript= import './App.css'; import React,{useState} from 'react'; import ClassContextComponent from './ClassContextComponent' export const ThemeContext = React.createContext(); export default function App() { const [darkTheme,setDarkTheme] = useState(true); // 這邊函式很簡單就是開關 darkTheme true/false 並且當作 props 傳下去 function toggleTheme (){ setDarkTheme(prevDarkTheme => !prevDarkTheme) } return ( <ThemeContext.Provider value={darkTheme}> <button onClick={toggleTheme}>Toggle Theme</button> <ClassContextComponent></ClassContextComponent> </ThemeContext.Provider> ); } ``` ### ClassContextComponent.js 這邊要接收來自 parent component 的 props 1. 首先要引入 ThemeContext 2. 使用 Consumer 包裹住 JSX 如此一來就可以使用傳下來的 props darkTheme 摟! ```javascript= import React,{Component} from 'react' import {ThemeContext} from './App' export default class ClassContextComponent extends Component { themeStyles(dark){ return { backgroundColor:dark? '#333' :'#ccc', color:dark? '#ccc':'#333', padding:'2rem', margin:'2rem' } } render(){ return( <ThemeContext.Consumer> {darkTheme =>{ return <div style={this.themeStyles(darkTheme)}> Class Theme</div> }} </ThemeContext.Consumer> ) } } ``` ### 小結 使用 context API 的好處就是 props 傳遞上面非常方便,不需要傳遞過去經過的 component ,只需要引入 createContext 的變數即可使用 但是整個過程操作上是比較麻煩的,接下來我們來看看 funciont component 會如何操作 useContext hook 簡化整個過程 ## Function component 中的 useContext ### FunctionContextComponent.js 是不是明顯簡潔超級多 不需要再使用 comsumer 來包裹著 JSX 了,直接使用 useContext 解構出 darkTheme ,再把 darkTheme 擺入 styles 裡面就完事了,超級好用 ```javascript= import React,{useContext} from 'react' import {ThemeContext} from './App' export default function FunctionContextComponent() { const darkTheme = useContext(ThemeContext) const themeStyles = { backgroundColor:darkTheme? '#333' :'#ccc', color:darkTheme? '#ccc':'#333', padding:'2rem', margin:'2rem' } return ( <div style={themeStyles}>Function Theme</div> ) } ``` ### App.js 這邊操作的方式跟 class component 的 parent component 操作方式是一樣的 1. 因為要操作 FunctionContextComponent 所以要記得引入 2. JSX 中也要記得引入 ```javascript= import './App.css'; import React,{useState} from 'react'; import FunctionContextComponent from './FunctionContextComponent' import ClassContextComponent from './ClassContextComponent' export const ThemeContext = React.createContext(); export default function App() { const [darkTheme,setDarkTheme] = useState(true); function toggleTheme (){ setDarkTheme(prevDarkTheme => !prevDarkTheme) } return ( <ThemeContext.Provider value={darkTheme}> <button onClick={toggleTheme}>Toggle Theme</button> <FunctionContextComponent></FunctionContextComponent> <ClassContextComponent></ClassContextComponent> </ThemeContext.Provider> ); } ``` 他們可以做到一樣的效果 ![](https://i.imgur.com/49nxRkQ.png) ## 運用 custom hook 繼續簡化程式碼 > 簡化程式碼的目的就是 Don't repeat yourself 首先建立一個新的檔案 ### ThemeContext.js 我們一步步解釋這邊做了什麼 1. 我想把 ThemeContext.Provider 拉出來 把 darkTheme 內容拉進此資料夾並且使用 `createContext()` 為了可以從這個檔案輸出給 children component 2. 因為我想把按鈕以及裡面的函式(toggleTheme)拉出來 把 toggleTheme 函式拉近來這個檔案並且並且使用 `createContext()` 為了可以從這個檔案輸出給 children component 3. 建立兩個 costom hook 來返回 useContext 這樣就可以把這些重複的程式碼都寫在這個 ThemeContex.js 內,並且做 export 就可以在其他 component 內使用摟 5. 在 JSX 使用上全部的 Provider 並且填入 value(就是要傳進去的 props)包裹著 children 這邊的 children 代表裡面包裹著的 component 7. 把整個 ThemeProvider 輸出到 App.js 就可以大大簡化其內容了 ```javascript= import React,{useState,useContext} from 'react'; const ThemeContext = React.createContext(); const ThemeUpdateContext = React.createContext(); export function useTheme(){ return useContext(ThemeContext) } export function useThemeUpdate(){ return useContext(ThemeUpdateContext) } export function ThemeProvider ({children}){ // 下面的 useState 以及 toggle 函式,是因為要簡化 App.js 內容被拉進來 const [darkTheme,setDarkTheme] = useState(true); function toggleTheme (){ setDarkTheme(prevDarkTheme => !prevDarkTheme) } return ( <ThemeContext.Provider value={darkTheme}> <ThemeUpdateContext.Provider value={toggleTheme}> {children} </ThemeUpdateContext.Provider> </ThemeContext.Provider> ) } ``` ### App.js 這簡潔的程式碼是否看了就開心? 剛剛那一堆程式碼都被擷取成短短個 ThemeProvider tag 就完事了 ```javascript= import React from 'react'; import FunctionContextComponent from './FunctionContextComponent' import {ThemeProvider} from './ThemeContext'; export default function App() { return ( <ThemeProvider> <FunctionContextComponent></FunctionContextComponent> </ThemeProvider> ); } ``` ### FunctionContextComponent.js 接下來看看這邊要怎麼使用調整過後的 useContext,以及讓裝進去的按鈕跑得動 1. 首先 useContext 因為我整個拉到 ThemeContext.js 內操作 cumstom hook 了所以這邊就引入他們即可 操作方式也很簡單直接解構出其中的 value 就可以直接使用,也就是 darkTheme, toggleTheme 兩個 2. 接下來就把引進來的 props 放入去的位置即可,程式就可以正常跑摟! ```javascript= import React from 'react' import { useTheme,useThemeUpdate } from './ThemeContext' export default function FunctionContextComponent() { const darkTheme = useTheme(); const toggleTheme = useThemeUpdate(); const themeStyles = { backgroundColor:darkTheme? '#333' :'#ccc', color:darkTheme? '#ccc':'#333', padding:'2rem', margin:'2rem' } return ( <> <button onClick={toggleTheme}>Toggle Theme</button> <div style={themeStyles}>Function Theme</div> </> ) } ```