react-redux 简单封装

react-redux 简单封装

安装

npm install --save redux
npm install --save react-redux
npm install --save-dev redux-devtools

依照官方实例 Todo List

处理 reducers

// 官方例子
const todos = (state = [], action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          id: action.id,
          text: action.text,
          completed: false
        }
      ]
    case 'TOGGLE_TODO':
      return state.map(todo =>
        (todo.id === action.id) 
          ? {...todo, completed: !todo.completed}
          : todo
      )
    default:
      return state
  }
}

export default todos
// 处理完的 todos.ts
// store/reducers/todos.ts
// 使用对象进行匹配,可将 actions 直接去掉
export interface TodoItem {
  id: number
  text: string
  completed: boolean
}

export type TodoStateProps = TodoItem[]
let nextTodoId = 0
const mutations: { [key: string]: (state: TodoStateProps, payload: unknown) => TodoStateProps } = {
  'ADD_TODO': (state: TodoStateProps, payload: unknown): TodoStateProps => {
    const action = payload as TodoItem
    return [
      ...state,
      {
        id: nextTodoId++,
        text: action.text,
        completed: false
      }
    ]
  },
  'TOGGLE_TODO': (state: TodoStateProps, payload: unknown): TodoStateProps => {
    const action = payload as TodoItem
    return state.map(todo =>
      (todo.id === action.id)
        ? { ...todo, completed: !todo.completed }
        : todo
    )
  }
}

const todos = (state: TodoStateProps = [], action: { type: string }): TodoStateProps => {
  const { type, ...payload } = action
  if (mutations[type]) {
    return mutations[type](state, payload)
  } else {
    return state
  }
}

export default todos
// 调用方式
import React from 'react'
import { connect, DispatchProp } from 'react-redux'

let AddTodo = ({ dispatch }: DispatchProp<any>) => {
  let input: HTMLInputElement | null

  return (
    <div>
      <form
        onSubmit={e => {
          e.preventDefault()
          if (input) {
            if (!input.value.trim()) {
              return
            }
            const timer = setTimeout(() => {
              if (input) {
                clearTimeout(timer)
                dispatch({ type: 'ADD_TODO', text: input.value }) // 直接传入一个 action 对象即可
                input.value = ''
              }
            }, 1000)
          }
        }}
      >
        <input
          ref={node => {
            input = node
          }}
        />
        <button type="submit">
          Add Todo
        </button>
      </form>
    </div>
  )
}

export default connect()(AddTodo)

处理 component

// utils/index.ts
// 组件处理函数
import { PropsWithChildren } from 'react'
import { connect, Matching } from 'react-redux'
import { AnyAction } from 'redux'
import { stateProps, actions } from '../store/reducers'

// 执行异步的 reducers
const handleDispatchAction = async (dispatch: any) => {
  return (action: AnyAction) => {
    const { type, ...payload } = action
    if (actions[type]) {
      await actions[type](dispatch, payload)
    } else {
      throw new Error(`${type} 没找到对应的 action`)
    }
  }
}
// 规定类型
export type dispatchActionType = (action: AnyAction) => void

type PropsType<T> = PropsWithChildren<Matching<{ storeState: stateProps; ownProps: T; } & { dispatch: any; dispatchAction: dispatchActionType; }, T>>
// 使用泛型来规定 props 参数
export const componentConnect = <T> (component: (props: PropsType<T>) => JSX.Element) => {
  	// 将 state, dispatch, dispatchAction 通过函数参数的方式注入到函数内
    const mapStateToProps = (state: stateProps, ownProps: Omit<T, 'storeState' | 'dispatch' | 'dispatchAction'>) => {
    return {
      storeState: state,
      ownProps
    }
  }

  const mapDispatchToProps = async (dispatch: any) => {
    return {
      dispatch,
      dispatchAction: await handleDispatchAction(dispatch)
    }
  }

  const connectComponent = connect(
    mapStateToProps,
    mapDispatchToProps
  )(component)

  return connectComponent
}
以 Link.js 为例
// 官方
// 通过组件加容器的方式进行处理
// components/Link.js
import React from 'react'
import PropTypes from 'prop-types'

const Link = ({ active, children, onClick }) => {
  if (active) {
    return <span>{children}</span>
  }

  return (
    <a
      href=""
      onClick={e => {
        e.preventDefault()
        onClick()
      }}
    >
      {children}
    </a>
  )
}

Link.propTypes = {
  active: PropTypes.bool.isRequired,
  children: PropTypes.node.isRequired,
  onClick: PropTypes.func.isRequired
}

export default Link
// 官方
// containers/FilterLink.js
import { connect } from 'react-redux'
import { setVisibilityFilter } from '../actions'
import Link from '../components/Link'

const mapStateToProps = (state, ownProps) => {
  return {
    active: ownProps.filter === state.visibilityFilter
  }
}

const mapDispatchToProps = (dispatch, ownProps) => {
  return {
    onClick: () => {
      dispatch(setVisibilityFilter(ownProps.filter))
    }
  }
}

const FilterLink = connect(
  mapStateToProps,
  mapDispatchToProps
)(Link)

export default FilterLink
经过处理后的
import { MouseEventHandler, ReactNode } from 'react'
import { componentConnect } from '../utils'
import { stateProps } from '../store/reducers'
interface LinkProps { children: ReactNode, filter: string, storeState: stateProps, dispatch: any }

const Link = ({ storeState, children, dispatch, ...ownProps }: LinkProps) => {
  if (storeState?.visibilityFilter === ownProps.filter) {
    return <span>{children}</span>
  }
  const handleClick: MouseEventHandler<HTMLButtonElement> = (e) => {
    e.preventDefault()
    dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter })
  }
  return (
    <>
      <button onClick={handleClick}>
        {children}
      </button>
    </>
  )
}

export default componentConnect<LinkProps>(Link) // 通过泛型传入 props 参数, 可在组件直接使用

处理异步的 reducers

以 visibilityFilter.js 为例

export type visibilityFilterStateProps = string

const mutations: { [key: string]: (state: visibilityFilterStateProps, payload: unknown) => visibilityFilterStateProps } = {
  'SET_VISIBILITY_FILTER': (state, payload) => {
    const action = payload as { filter: string }
    return action.filter
  }
}

// 添加一个处理异步的 actions
export const actions = {
  'SYNC_SET_VISIBILITY_FILTER': async (dispatch: any, payload: unknown) => {
    const params = payload as Object
    await new Promise<void>((resolve, reject) => {
      const timer = setTimeout(() => {
	    clearTimeout(timer)
	    dispatch({ type: 'SET_VISIBILITY_FILTER', ...params })
	    resolve()
	  }, 1000)
    })
  }
}

const visibilityFilter = (state: visibilityFilterStateProps = 'SHOW_ALL', action: { type: string, payload: unknown }): visibilityFilterStateProps => {
  const { type, ...payload } = action
  if (mutations[type]) {
    return mutations[type](state, payload)
  } else {
    return state
  }
}

export default visibilityFilter
核心处理部分
// 在 store/reducers/index.ts 中
// 使用 webpack 的 require.context 载入各个 reducers
// ts 环境记得添加 webpack 类型 npm i @types/webpack-env @types/node -D
const modules = require.context('.', false, /^\.\/(?!index)\w+.ts$/)

// 组合,导出 reducers 的 actions
export const actions = modules.keys().reduce((pre: { [key: string]: (dispatch: any, ...params: any) => void }, key: string) => {
  return Object.assign(pre, modules(key).action || {})
}, {})

// 在 utils/index.ts 中
// 导入 actions
import { stateProps, actions } from '../store/reducers'
// 执行异步的 reducers
const handleDispatchAction = async (dispatch: any) => {
  return (action: AnyAction) => {
    const { type, ...payload } = action
    if (actions[type]) {
      await actions[type](dispatch, payload)
    } else {
      throw new Error(`${type} 没找到对应的 action`)
    }
  }
}

const mapDispatchToProps = async (dispatch: any) => {
    return {
        dispatch,
        dispatchAction: await handleDispatchAction(dispatch) // 将 dispatchAction 一起注入到函数内
    }
}
使用
import { MouseEventHandler, ReactNode } from 'react'
import { componentConnect, dispatchActionType } from '../utils'
import { stateProps } from '../store/reducers'
interface LinkProps { children: ReactNode, filter: string, storeState: stateProps, dispatch: any, dispatchAction: dispatchActionType }

const Link = ({ storeState, children, dispatch, dispatchAction, ...ownProps }: LinkProps) => {
  if (storeState?.visibilityFilter === ownProps.filter) {
    return <span>{children}</span>
  }
  const handleClick: MouseEventHandler<HTMLButtonElement> = async (e) => {
    e.preventDefault()
    await dispatchAction({ type: 'SYNC_SET_VISIBILITY_FILTER', filter: ownProps.filter }) // 使用 dispatchAction 进行调用
    // dispatch({ type: 'SET_VISIBILITY_FILTER', filter: ownProps.filter })
  }
  return (
    <>
      <button onClick={handleClick}>
        {children}
      </button>
    </>
  )
}

export default componentConnect<LinkProps>(Link)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值