Bootstrap

React Hooks之useReducer

一、useReducer

const [state, dispatch] = useReducer(reducer, initialArg, init?)
  • useState 的替代方案。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
  • 数据结构简单用useState,复杂时用useReducer
  • 简化版的redux
  • 结合context解决跨组件的问题
  • state dispatch默认没有模块化,数据混在一起

  • state或store:数据
  • action:命令
  • reducer: 规定,根据action的type做相应处理返回新的state
  • dispatch: 派发action

二、基本使用

CounterReducer.tsx

// import React, { FC, useState } from 'react' //注释的是使用useState的情况
import React, { FC, useReducer } from 'react'

type StateType = { count: number }
type ActionType = { type: string }

const initialState: StateType = { count: 100 } // 初始值100

// 根据传入的action 返回新的state(不可变数据)
function reducer(state: StateType, action: ActionType) {
  switch(action.type) {
    case 'increment':
      return { count: state.count + 1 }
    case 'decrement':
      return { count: state.count - 1 }
    default:
      throw new Error()
  }
}

const CountReducer:FC = () => {
  // const [count, setCount] = useState(100)
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    // <>
    //   <p>coint: {count}</p>
    //   <button onClick={() => setCount(count + 1)}>+</button>
    //   <button onClick={() => setCount(count - 1)}>-</button>
    // </>
    <>
      <p>coint: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
    </>
  )

}

export default CountReducer

三、Todo List By useReducer项目

在这里插入图片描述
使用Context和useReducer实现跨组件通讯
代码地址:点击跳转

store.ts
在这里插入图片描述
reducer.tsx
在这里插入图片描述
List.tsx

import React, { FC, useContext } from 'react'
// import reducer from './reducer'
// import initialState from './store'
import { TodoContext } from '.'

const List:FC = () => {
  // const [state, dispatch] = useReducer(reducer, initialState)
  // const [state, dispatch] = useReducer(reducer, initialState)
  const context = useContext(TodoContext)
  const { state, dispatch } = context
  

  const del = (id: string) => {
    dispatch({
      type: 'delete',
      payload: id
    })
  }
  
  return (
    <>
      <p>list</p>
      <ul>
        {
          state.map(item => {
            return <li key={item.id}>
              <span>{item.title}</span>
              <button onClick={() => del(item.id)}>删除</button>
            </li>
          })
        }
      </ul>
    </>
  )

}

export default List

InputForm.tsx

import React, { FC, ChangeEvent, useState, useContext } from 'react'
// import reducer from './reducer'
// import initialState from './store'
import { nanoid } from 'nanoid'
import { TodoContext } from '.'

const InputForm:FC = () => {
  // const [state, dispatch] = useReducer(reducer, initialState)
  const context = useContext(TodoContext)
  const { state, dispatch } = context

  // 输入框文字
  const [text, setText] = useState('')
  const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
    setText(event.target.value)
  }
  // 新增todo
  const handleSubmit = (event: ChangeEvent<HTMLFormElement>) => {
    event.preventDefault()
    if (!text.trim()) return
    const newTodo = {
      id: nanoid(5),
      title: text
    }
    dispatch({
      type: 'add',
      payload: newTodo
    })
    setText('')
    
  }

  return (
    <form onSubmit={handleSubmit}>
      <label htmlFor="new-todo">what needs to be done?</label>
      <br />
      <input id='new-todo' onChange={handleChange} value={text} />
      <button type='submit'>Add #{state.length + 1}</button>
    </form>
  )

}

export default InputForm

index.tsx

import React, { FC, createContext, useReducer } from 'react'
import List from './List'
import InputForm from './InputForm'
import reducer, { ActionType } from './reducer'
import initialState from './store'

export const TodoContext = createContext({
  state: initialState,
  // eslint-disable-next-line
  dispatch: ( action: ActionType ) => {/* 空 */}
})

const Demo:FC = () => {
  const [state, dispatch] = useReducer(reducer, initialState)

  return (
    <TodoContext.Provider value={{ state, dispatch }}>
      <p>Todo list by reducer</p>
      <List />
      <InputForm />
    </TodoContext.Provider>
  )

}

export default Demo
;