# [React] Custom Context Provider (Context API) ###### tags: `react` ## What is React's context | React context是什麼 > Context provides a way to pass data through the component tree without having to pass props down manually at every level. By [React](https://zh-hant.reactjs.org/docs/context.html) Context 是一種用來傳遞資料的方式,讓我們不需要使用Props一層一層傳遞 在開始使用前我們要先建立這些相當於整個網頁app的全域變數,並做成一個Provider以供其他component使用 ``` import { createContext } from 'react' const Context = createContext(); ``` 稍後我們建立這個Provider時會將將這個Context當成一個Component回傳,並且將這些變數包在value props當中,這些變數始得被使用。 根據React官方文件的寫法如下 ``` <Context.Provider value = {/* some value */} > ``` 若覺得在Component當中加入 .Provider 較不直覺,亦可在宣告時處理 ``` import { createContext } from 'react' const Context = createContext(); ContextProvider = Context.Provider // 稍後會使用的回傳值則會變成ContextProvider <ContextProvider value = {/* some value */} > ``` ## Provider當中的變數宣告與使用 ### 定義Provider 在這裡將Provider命名為StateProvider,並將Props.children傳入,故它可以把整個網頁app包起來 ``` // state.js function StateProvider( {children} ){ const id = 10; return (<ContextProvider value = {{id}} > { children } </ContextProvider> ); } ``` ### 定義Consumer 將consumer寫成一個hook,以便在其他的component當中輕易的使用 前面在做Provider時使用了```createContext()``` 而現在要使用(Consume),則會使用```useContext()``` ``` // state.js import { useContext } from 'react' function useId(){ return useContext(Context) } ``` ### 使用 先將整個app用這個Provider包起來 ``` // _app.js import { ContextProvider } from '/path/to/state.js' function MyApp({ Component, pageProps }) { return ( <ContextProvider> <Page> <Component {...pageProps} /> </Page> </ContextProvider> ); } ``` 而在要使用的頁面(page)或component當中 ``` // index.js import { useId } from '/path/to/state.js' export default function Index(){ const { id } = useId() return ( <div> {id} </div> ) } ``` ## Example 範例 : Shopping Cart's State 購物車的狀態(開啟open/關閉close) ### 搭配State Hook ```useState()``` #### cartState.js ``` // cartState.js import { createContext, useContext, useState } from 'react'; const LocalStateContext = createContext(); const LocalStateProvider = LocalStateContext.Provider; function CartStateProvider({ children }) { const [cartOpen, setCartOpen] = useState(false); function toggleCart() { setCartOpen(!cartOpen); } function closeCart() { setCartOpen(false); } function openCart() { setCartOpen(true); } return ( <LocalStateProvider value={{ cartOpen, setCartOpen, toggleCart, closeCart, openCart }} > {children} </LocalStateProvider> ); } function useCart() { const all = useContext(LocalStateContext); return all; } export { CartStateProvider, useCart }; ``` #### _app.js ``` // _app.js import { CartStateProvider } from '../lib/cartState'; function MyApp({ Component, pageProps }) { return ( <CartStateProvider> <Page> <Component {...pageProps} /> </Page> </CartStateProvider> ); } ``` #### Cart.js ``` // Cart.js import { useCart } from '../lib/cartState'; export default function Cart(){ const { cartOpen, closeCart } = useCart(); return ( <div open={cartOpen}> <header> <h2>Cart</h2> <button type="button" onClick={closeCart}> &times; </button> </header> </div> ); }