天天看點

使用useReducer和useContext管理元件

useReducer

useState

可以用最簡單的方式更新狀态,但是狀态更新的邏輯散落在UI中,不能獨立複用,也不便于測試。使用

useReducer

來對一個元件的狀态進行管理就可以避免這一情況。

import React, { useState, useReducer } from 'react'

const initialState = []

const reducer = (state, action) => {
  const [type, value] = action
  const copy = [...state]
  switch (type) {
    case 'add':
      copy.push(value)
      return copy
    case 'del':
      copy.splice(value, 1)
      return copy
    default:
      return copy;
  }
}

export default TodoList = () => {
  const [state, dispatch] = useReducer(reducer, initialState)
  const [value, setValue] = useState('')

  return (
    <div>
      <h2>list</h2>
      <input type="text" onBlur={(e) => setValue(e.target.value)} />
      <button onClick={() => dispatch(['add', value])}>+</button>
      <ul>
        {
          state.map((item, index) => (
            <li key={index}>
              <span>{index + 1}:{item}</span>
              <button onClick={() => dispatch(['del', index])}>-</button>
            </li>
          ))
        }
      </ul>
    </div>
  )
}
           

這樣我們就完成了一個簡單的TodoList元件

使用useReducer和useContext管理元件

useContext

useReducer

雖然很好地分離了邏輯和UI,但是無法像redux一樣進行跨元件的狀态共享,在某些情況下如如果元件的狀态都使用redux進行管理會導緻redux比較混亂,是以針對比較複雜的元件我們使用

useContext

來實作跨元件的狀态共享可以很好的避免這些情況。

//index
import React, { useReducer } from 'react'
import Input from './input'
import List from './list'

export const TodoContext = React.createContext({})

const initialState = []

const reducer = (state, action) => {
  const [type, value] = action
  const copy = [...state]
  switch (type) {
    case 'add':
      copy.push(value)
      return copy
    case 'del':
      copy.splice(value, 1)
      return copy
    default:
      return state;
  }
}

export default () => {
  const [state, dispatch] = useReducer(reducer, initialState)
  return (
    <div>
      <h2>list</h2>
      <p>Number:{state.length}</p>
      //在頂層元件中下發state和dispatch
      <TodoContext.Provider value={{ state, dispatch }}>
        <Input />
        <List />
      </TodoContext.Provider>
    </div>
  )
}
           
//input
import React, { useContext, useState } from 'react'
import {TodoContext} from './index'
export default  () => {
  //下層元件使用useContext擷取頂層元件下發的state和dispatch
  const {dispatch} = useContext(TodoContext)
  const [value, setvalue] = useState('')
  return (
    <div>
      <input type="text" onBlur={(e) => setvalue(e.target.value)} />
      <button onClick={() => dispatch(['add', value])}>+</button>
    </div>
  )
}
           
//list
import React, { useContext } from 'react'
import Todo from './todo'
import { TodoContext } from './index'

export default () => {
//下層元件使用useContext擷取頂層元件下發的state和dispatch
  const { state } = useContext(TodoContext)
  return (
    <ul>
      {
        state.map((item, index) => <Todo item={item} index={index} key={index} />)
      }
    </ul>
  )
}
           
//todo
import React, { useContext } from 'react'
import { TodoContext } from './index'
export default  ({ index, item }) => {
  //下層元件使用useContext擷取頂層元件下發的state和dispatch
  const {dispatch} = useContext(TodoContext)
  return (
    <li key={index}>
      <span>{index + 1}:{item}</span>
      <button onClick={() => dispatch(['del', index])}>-</button>
    </li>
  )
}
           

在元件頂層使用

useReducer

統一管理狀态和邏輯,下層元件隻負責UI部分,這樣元件的狀态管理就變得很清晰了。每一個元件都會有一套内部的狀态管理,元件登出時該元件的狀态也就登出了,不會像redux一樣沒有使用的狀态也依然存在無法登出。

使用useReducer和useContext管理元件