React中的useReducer设计思想与Vue3中使用

React中的useReducer

我们在写逻辑的时候,通常是一个业务一个方法,这样使得代码的逻辑看上去会很复杂,所以在React中提出了useReducer来进行方法的结构,目的时使用这种方式实现方法的复用,使得项目的扩展性更强。

我们通过一个例子来展示useReducer的好处

在React中常规todoList的写法

构思组件

根据分析,我们可以把组件分成两个部分,头部输入框以及主要数据展示区

于是我们可以构建一下两个组件对其业务进行分析以及编写

index.jsx为两个子组件的父组件,用于统一两个组件并一同注册与App.js中

完成组件业务功能

import React, { useState } from 'react'

function TodoForm(props) {
  const [todoText, setTodoText] = useState('')
  const { onAddTodo } = props

  const addTodo = () => {
    onAddTodo({
      id: new Date().getTime(),
      content: todoText,
      completed: false
    })

    setTodoText('')
  }

  return (
    <div>
      <input
        type="text"
        placeholder="Please type something"
        value={todoText}
        onChange={(e) => setTodoText(e.target.value)}
      />
      <button onClick={addTodo}>ADD ITEM</button>
    </div>
  )
}

export default TodoForm
function TodoList(props) {
  const { todoList, onToggleTodo, onRemove } = props

  return (
    <ul>
      {todoList &&
        todoList.map((item) => (
          <li key={item.id}>
            <input
              type="checkbox"
              checked={item.completed}
              onChange={() => onToggleTodo(item.id)}
            />
            <span
              style={{
                textDecoration: item.completed ? 'line-through' : 'none'
              }}
            >
              {item.content}
            </span>
            <button
              onClick={() => {
                onRemove(item.id)
              }}
            >
              remove
            </button>
          </li>
        ))}
    </ul>
  )
}

export default TodoList

import React, { useCallback, useState } from 'react'
import TodoForm from './Form'
import TodoList from './List'

function TodoApp() {
  const [todoList, setTodoList] = useState([])

  const addTodo = useCallback((todo) => {
    setTodoList((todoList) => [...todoList, todo])
  }, [])

  const toggleTodo = useCallback((id) => {
    setTodoList((todoList) =>
      todoList.map((item) => {
        if (item.id === id) {
          item.completed = !item.completed
        }
        return item
      })
    )
  }, [])
  
  const removeTodo = useCallback((id) => {
    setTodoList((todoList) => todoList.filter((item) => item.id !== id))
  }, [])

  return (
    <div>
      <TodoForm onAddTodo={addTodo}></TodoForm>
      <TodoList
        todoList={todoList}
        onToggleTodo={toggleTodo}
        onRemove={removeTodo}
      ></TodoList>
    </div>
  )
}

export default TodoApp

从上面的方法我们可以看出,一旦方法多了,就会出现业务逻辑不够清晰的情况,方法混乱,后期维护会越来越困难

于是React的开发人员也想到了这一点,设计出了useReducer Hook来解决这一问题

下面我们看看使用useReducer Hook 如何解决这类问题

我们的两个组件无需做任何改变,重点改变在index.jsx

import React, { useReducer } from 'react'
import TodoForm from './Form'
import TodoList from './List'
import actionTypes from './store/actionTypes'

import { todoReducer } from './store/reducer'
import { initialTodoList } from './store/state'
function TodoApp() {
  const [todoList, todoDispatch] = useReducer(todoReducer, initialTodoList)

  return (
    <div>
      <TodoForm
        onAddTodo={(todo) =>
          todoDispatch({ type: actionTypes.ADD_LIST, payLoad: todo })
        }
      ></TodoForm>
      <TodoList
        todoList={todoList}
        onToggleTodo={(id) =>
          todoDispatch({ type: actionTypes.TOGGLE_LIST, payLoad: id })
        }
        onRemove={(id) => {
          todoDispatch({ type: actionTypes.REMOVE_LIST, payLoad: id })
        }}
      ></TodoList>
    </div>
  )
}

export default TodoApp

在todolist文件夹中创建一个文件夹store来完成我们对应的业务拆解

了解React中useReducer的应该都知道 useReducer的使用要求我们传入一个回调函数以及数据的初始值

我们朝着这方面开始设计程序

在store文件夹中创建state.js用于集中导出我们需要导出的我们的数据初始值

const initialTodoList = []

export { initialTodoList }

在store文件夹中创建actionTypes.js创建文件主要目的是为了后期的代码维护,并且可以更好的提交编码的效率

const actionTypes = {
  ADD_LIST: 'ADD_TODO',
  TOGGLE_LIST: 'TOGGLE_LIST',
  REMOVE_LIST: 'REMOVE_LIST'
}

export default actionTypes

在store文件中创建reducer.js,创建这个文件主要是一个type分发器,主要逻辑是根据type的不同完成实现不同个方法

import actionTypes from './actionTypes'
import { addTodo, toggleTodo, removeTodo } from './todoFunc'

function todoReducer(todoList, action) {
  const { type, payLoad } = action

  switch (type) {
    case actionTypes.ADD_LIST:
      return addTodo(todoList, payLoad)
    case actionTypes.TOGGLE_LIST:
      return toggleTodo(todoList, payLoad)
    case actionTypes.REMOVE_LIST:
      return removeTodo(todoList, payLoad)
    default:
      break
  }
}

export { todoReducer }

在store文件中todoFunc.js,主要用于创建我们现在需要的具体方法

function addTodo(todoList, payLoad) {
  return [...todoList, payLoad]
}

function toggleTodo(todoList, payLoad) {
  return todoList.map((item) => {
    if (item.id === payLoad) {
      item.completed = !item.completed
    }
    return item
  })
}

function removeTodo(todoList, payLoad) {
  return todoList.filter((item) => item.id != payLoad)
}

export { addTodo, toggleTodo, removeTodo }

在经过这样书写之后我们还是可以完成响应的业务,代码看上去复杂了,但是我们实则已经将代码进行了解耦,是代码的可维护性更强,是代码更加健壮,易于扩展

有了React的基础,让我有了一种思考,是否可以把这种设计思想用在我们的Vue3中呢。答案是可以的,说做就做,下面我来展示在Vue3完成类似于React中useReducer的Hook

使用Vue3实现类似于React中useReducer的Hooks

了解到在React中的userReducer是返回一个数组,数组中可以结构出我们需要数据的响应式数据,以及一个分发器Dispatch

那么我们在src目录下创建一个文件夹Hooks用来写我们自定的Hooks

创建useReducer.js

import { ref } from 'vue'
function useReducer(reducer, initialState) {
  const state = ref(initialState)

  const action = {}

  function dispatch({ type, payLoad }) {
    action.type = type
    action.payLoad = payLoad
    reducer(state, action)
  }
  return [state, dispatch]
}

export default useReducer

现在我们有了类似于React中useReducer的Hook

那么我们就可以模仿前面的代码进行代码编写

在Components文件夹下创建todoList,用来编写我们的TodoList业务逻辑

下面直接上代码

<template>
  <div>
    <input type="text" placeholder="please type somthing" v-model="todoText" />
    <button @click="addTodo">ADD ITEM</button>
  </div>
</template>
<script setup>
import { ref } from 'vue'
const emits = defineEmits(['onAddTodo'])
const todoText = ref('')

const addTodo = () => {
  if (todoText.value !== '') {
    emits('onAddTodo', {
      id: new Date().getTime(),
      content: todoText.value,
      completed: false
    })
  } else {
    alert('输入不能为空')
  }

  todoText.value = ''
}
</script>
<style></style>
<template>
  <ul>
    <li v-for="item of props.todoList" :key="item.id">
      <input
        type="checkbox"
        :checked="item.completed"
        @click="toggleTodo(item.id)"
      />
      <span
        :style="{ textDecoration: item.completed ? 'line-through' : 'none' }"
        >{{ item.content }}</span
      >
      <button @click="removeTodo(item.id)">REMOVE</button>
    </li>
  </ul>
</template>
<script setup>
const props = defineProps({
  todoList: {
    type: Array,
    default() {
      return []
    }
  }
})
const emits = defineEmits(['onToggleTodo', 'onRemoveTodo'])
const toggleTodo = (id) => {
  emits('onToggleTodo', id)
}

const removeTodo = (id) => {
  emits('onRemoveTodo', id)
}
</script>
<style></style>
<template>
  <div>
    <todo-form
      @onAddTodo="
        (todo) => todoDispatch({ type: actionTypes.ADD_LIST, payLoad: todo })
      "
    />
    <todo-list
      :todoList="todoList"
      @onToggleTodo="
        (id) => todoDispatch({ type: actionTypes.TOGGLE_LIST, payLoad: id })
      "
      @onRemoveTodo="
        (id) => todoDispatch({ type: actionTypes.REMOVE_LIST, payLoad: id })
      "
    />
  </div>
</template>
<script setup>
import TodoForm from './Form.vue'
import TodoList from './List.vue'
import { todoReducer, actionTypes } from './store'
import { useReducer } from '../../hooks'

const [todoList, todoDispatch] = useReducer(todoReducer, [])
</script>
<style></style>

同样我们还是需要想React项目中一样的业务逻辑代码

这里我就直接上代码了

const actionTypes = {
  ADD_LIST: 'ADD_TODO',
  TOGGLE_LIST: 'TOGGLE_LIST',
  REMOVE_LIST: 'REMOVE_LIST'
}

export default actionTypes

import actionTypes from './actionTypes'
import { todoReducer } from './reducer'

export { actionTypes, todoReducer }
import actionTypes from './actionTypes'
import { addTodo, toggleTodo, removeTodo } from './todoFunc'

function todoReducer(state, action) {
  const { type, payLoad } = action

  switch (type) {
    case actionTypes.ADD_LIST:
      return addTodo(state, payLoad)
    case actionTypes.TOGGLE_LIST:
      return toggleTodo(state, payLoad)
    case actionTypes.REMOVE_LIST:
      return removeTodo(state, payLoad)
    default:
      break
  }
}

export { todoReducer }
function addTodo(state, payLoad) {
  state.value.push(payLoad)
}

function toggleTodo(state, payLoad) {
  state.value = state.value.map((item) => {
    if (item.id === payLoad) {
      item.completed = !item.completed
    }
    return item
  })
}

function removeTodo(state, payLoad) {
  state.value = state.value.filter((item) => item.id !== payLoad)
}

export { addTodo, toggleTodo, removeTodo }

总结

经过上面的论述,我们已经基本实现了React中的设计思路搬迁,在今后的学习过程中我还会继续分享我的想法!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值