iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
0
自我挑戰組

React初心者30天的探索之路系列 第 22

[Day 22]React hook(中)-useContext&useReducer

  • 分享至 

  • xImage
  •  

useContext()

useContext會和React Context API搭配使用,可以讓component共享資料,像是進階版的props,不用一層一層的傳props真的太美好了,可以隔山打牛,可以避免掉HOC的波動拳…不得不說這樣真的方便多了。

React Context API 

父層component的部分:

利用createContext建立 Context component,createContext 可傳入預設值,Context Provider需要包住整個組件,Context Provider的value記得要傳入需要傳遞的值,只要是被provider所包含的component都可以取得Content。

子層component的部分:

呼叫 useContext 的 component 會在 context 值更新時重新 render,以前是要用Context Consumer來取值,有了React Hooks後子組件就可以利用useContext來取得資料

const content = useContext(Context)

層級關係=> ContentExample > SideBar > SideBarButton(爺、父、孫)
在爺爺身上注入資料,孫子用useContext不透過父親也能拿到爺爺的value

const slogan =  'super star'

export const Content = createContext(slogan)
function ContextExample(props) {
    return (
        <Content.Provider value={slogan}>
            <SideBar /> 
         <Content.Provider>
    )
}

function SideBar(props) {
    return (
        <div>
            <SideBarButton />
        </div>
    )
}

function SideBarButton(props) {
    const title = useContext(Content)
    return (
        <div>
            {title}
        </div>
    )
}


此時可能會冒出一個疑問,明明我在createContext的已經傳入了預設值, 為什麼在Context Provider的value還要再傳一遍?我也是寫完才發現這件事,Context Provider的value為可傳也可不傳 ,但如果傳值就會以傳入的value為優先,而非預設值。

好,寫到這裡時剛好我看到一段關於useContext的教學影片,發現其實context.provider可以不用寫,心裡想說怎麼可能!因為我查到的所有範例都有寫context.provider,試著把context.provider拔掉居然還是可以正常運作?

教學影片的結論是使用useContext時將不再需要Provider&Consumer,不過這部分目前找不到相關資料佐證,先持保留態度好了

假設子component和父component不在同一個component裡面,如下

import React,{ createContext, useContext, Fragment} from 'react';
import ContextExample2 from  './ContextExample2'
const slogan =  'super star'

const Content = createContext(slogan)
function ContextExample(props) {
    return (
        <Fragment>
            <ContextExample2 content={Content} />
        </Fragment>    
   
    )
}

子component就可以透過props來取得context

import React, { useContext, Fragment } from 'react';
const ContextExample2  = (props) => {
    const context = useContext(props.content)
    return(
        <Fragment>
            {context}
        </Fragment>
    )    
    
}

export default ContextExample2

但這樣感覺還是沒有很方便,有其他的方法嗎?有的!把content export出來 再引入即可

ContextExample.js --將Content export

export const Content = createContext(slogan)

ContextExample2.js -- import Content

import {Content} from './ContextExample'
const ContextExample2  = () => {
    const context = useContext(Content)
    return(
        <Fragment>
            {context}
        </Fragment>
    )    
    
}

export default ContextExample2

但這樣寫,當createContext數量變多,就會變得有點混亂,所以建議新增一個store,裡面就專門存放createContext,方便管理

useReducer

再次回想到之前寫React-Redux被搞得暈頭轉向,要引入Redux、React-Redux,然後在寫對應的設定,對於一個React新手來說就像迷宮一樣,有種我現在到底在哪裡的錯覺, 還好現在有useReducer可以簡化這個流程

const [count, dispatch] = useReducer(reducer, initialState, initialAction)
  • reducer : 跟以往的reducer一樣, 先列出有哪些動作指令 ,並且根據不同的動作回傳操作過後的state
  • initialState:定義初始值
  • initialAction:為useReducer初次執行的action,但貌似不常用

將reducer和initialState傳入userReducer 並且以解構的方式回傳

第一個值為state,第二個值為dispatch 
接著只要透過dispatch就可以成功修改值,下面以一個簡單的計數器為例子,可以看得出來流程簡化了不少

import React, { useReducer, Fragment } from 'react';

const initialState = 0
const reducer = (state, aciton) =>{
    switch (aciton) {
        case 'increment':
            return state + 1
        case 'decrement':
            return state - 1
        default:
            return state
    }
}

function UseReducerExample(){
    const [count, dispatch] = useReducer(reducer, initialState)
    return (
        <Fragment>
            <h1>{count}</h1>
            <button onClick={() => {dispatch('increment')}}>+1</button>
            <button onClick={() => {dispatch('decrement')}}>-1</button>
        </Fragment>
    )
}

export default UseReducerExample

跟之前的React-Redux比起來,這個寫法真的親民的許多呀!


上一篇
[Day 21] React Hooks (上)-useState&useEffect
下一篇
[Day 23] React hook(下)-useMemo&useRef
系列文
React初心者30天的探索之路30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言