useReducer + Context 使用TS制作简易状态管理

76 阅读2分钟

先把代码记录下来,不注重讲解。

不懂的可以先了解一下基础知识

基本代码

类型定义

/store/type.d.ts

数据结构类型定义

export interface StateProps {
  DETAIL?: DataType
}

export type StatePropsKey = keyof StateProps

自定义reducer方法类型定义


interface updateAction {
  type: 'UPDATE'
  value: { field: StatePropsKey, data: any }
}

interface updateSomeAction {
  type: 'UPDATE_SOME'
  value: StateProps
}

interface clearAction {
  type: 'CLEAR'
}

export type ReducerActionProps = updateAction | updateSomeAction | clearAction

初始化数据结构

/store/initState.ts

import { StateProps } from './type'

// 初始状态
const initState: StateProps = {
  DETAIL: undefined
}

export default initState

useReducer 实现方法

/store/useReducerAction.ts

import { useReducer } from 'react'
import { cloneDeep } from 'lodash'

import initState from './initState'

import {
  StateProps,
  StatePropsKey,
  ReducerActionProps,
} from './type'

const reducer = (
  prevState: StateProps,
  action: ReducerActionProps,
) => {
  const { type } = action

  // 不改变原始state
  const state: StateProps = { ...prevState }

  switch (type) {

    // 更新
    case 'UPDATE':
      state[action.value.field] = action.value.data
      break

    // 更新部分
    case 'UPDATE_SOME':
      Object.keys(action.value || {}).forEach((key: any) => {
        const temp = key as StatePropsKey
        // @ts-ignore
        state[temp] = action.value[temp]
        // state[key as StatePropsKey] = action.value[key as StatePropsKey]
      })
      break

    // 清空
    case 'CLEAR':
      return cloneDeep(initState)

    default:
      throw new Error('无效类型')

  }

  return state
}

function useReducerAction () {
  // 使用useReducer将 reducer和initState结合
  const [ state, dispatch ] = useReducer(reducer, cloneDeep(initState))

  return {
    state,
    dispatch,
  }
}

export default useReducerAction

创建Context 并结合 useReducer 的实现方法

/store/context.tsx

import React, { createContext } from 'react'

import { cloneDeep } from 'lodash'

import useReducerAction from './useReducerAction'
import initState from './initState'

import { StateProps, ReducerActionProps } from './type'

const Context = createContext<{
  state: StateProps,
  dispatch: React.Dispatch<ReducerActionProps>
}>({
  state: cloneDeep(initState),
  dispatch: () => {
    throw new Error("GlobalContext 未定义")
  }
})

type Props = {
    children: React.ReactNode
}

export const Provider = (props: Props) => {
  const { state, dispatch } = useReducerAction()
  return (
    <Context.Provider value={{ state, dispatch }}>
      { props.children }
    </Context.Provider>
  )
}

export default {
  Context,
  Provider
}


加入状态管理

定义操作方法 /store/useAction.tsx

import { useContext } from 'react'

import context from './context'

import { StateProps, StatePropsKey } from './type'

/**
 * 事件处理
 *
 * 使用时请使用 hooks 方式,如useActions
 * @returns
 */
const actions = () => {
  const { state, dispatch } = useContext(context.Context)

  /**
   * 更新数据
   * @param field 数据类型
   * @param data 数据
   */
  const update = (field: StatePropsKey, data: any) => dispatch({ type: 'UPDATE', value: { field, data } })

  const updateSome = (data: StateProps) => dispatch({ type: 'UPDATE_SOME', value: data })

  const clear = () => dispatch({ type: 'CLEAR' })

  const init = () => {
    clear()
  }

  return {
    state,
    method: {
      update,
      updateSome,
      clear,
      init,
    }
  }
}

export default actions

根组件加入状态管理

import { FC, useEffect } from 'react'

import Context from './store/context'

interface CanvasProps {
}

const Canvas: FC<CanvasProps> = (props) => {
  const { state, method } = useAction()

  useEffect(() => {
    method.init()

    return () => {
      method.clear()
    }
  }, [])

  return <div></div>
}

const connect: FC<CanvasProps> = () => {
  return <Context.Provider>
    <Canvas />
  </Context.Provider>
}

export default connect