【案例】使用React+redux实现一个Todomvc

news2024/9/24 11:24:45

About
大家好,我是且陶陶,今天跟大家分享一个redux的todoList案例,通过这个案例能够快速掌握redux的基本知识点🌹

❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…❤️…

前情回顾 - 什么是redux 🪷

最流行的状态管理工具之一。(类似于 vue中的vuex)

Redux和React是两个独立的工具/

三个核心概念🌟

  1. action(动作/行为):【对象格式】描述要做的事(例如:登陆、退出、增删改查等等…)
  2. reducer(函数):【函数格式 function reducer(state = 0,action){ } 】更新状态
  3. store(仓库):整合action(动作)和reduce(函数)
    在这里插入图片描述

store分配要做的事actionreducer

🍬TodoMVC案例

代码地址🍻:
TodoMvc
欢迎大家批评指正~

功能介绍 🌺

🍦 添加事项
🍦 删除事项
🍦 完成or未完成事项
🍦 全选反选
🍦 清空

在这里插入图片描述

🍿 静态结构

在这里插入图片描述

🍰 状态管理 - redux

一、创建store📂

在这里插入图片描述

  1. store/reducer/todos.js 中处理行为

    const initList = [
      { id: 1, name: '学习日语,备考N1', isDone: true },
      { id: 2, name: '学习英语,备考雅思', isDone: false },
      { id: 3, name: '学习GO,找工作', isDone: false },
    ]
    export default function todosReducer(state = initList, sction) {
      return state
    }
    
  2. store/reducers/index.js 中合并单独的reducer并导出

    // 模块合并 并导出
    import todos from './todo'
    import { combineReducers } from 'redux'
    
    const rootReducer = combineReducers({ todos })
    export default rootReducer
    
    
  3. store/index.js中挂载 reducer和action

    // 创建仓库,挂载reducers 并导出
    import { createStore } from 'redux'
    import reducers from './reducers/index'
    // 创建store
    const store = createStore(reducers)
    export default store
    
    

二、引入redux🧊

index.jsx中,引入reduxreact-redux

用Provider包裹根组件,并提供store值

import ReactDOM from 'react-dom/client'
import App from './App'
import store from './store/index'
import { Provider } from 'react-redux'
import './styles/base.css'
import './styles/index.css'

// 渲染UI界面
const root = ReactDOM.createRoot(document.querySelector('#root'))
root.render(
  <Provider store={store}>
    <App></App>
  </Provider>
)

三、使用仓库状态📉

  1. components/TodoMain.jsx 【列表内容组件】中,使用 useSelector, useDispatch 这两个hook 操作状态。

    import React from 'react'
    import TodoItem from './TodoItem'
    import { useSelector, useDispatch } from 'react-redux'
    export default function TodoMain() {
      // 拿到状态
      const todos = useSelector((state) => state.todos)
      **console.log(todos)**
      // 修改状态
      const dispatch = useDispatch()
      ...
      ...
    

在这里插入图片描述

更改状态🍥

步骤

  1. 界面绑定onChange事件,dispatch触发行为。
  2. 定义一个action行为,声明actionType
  3. 根据行为在todosReducer中处理状态

功能实现🍹

界面渲染🕸️

渲染 事项📋

  1. TodoMain.jsx中。循环渲染todolist中的每一项。传递每一项item
 ...
 ...
 
  return (
    <section className="main">
      <input id="toggle-all" className="toggle-all" type="checkbox" />
      <label htmlFor="toggle-all">Mark all as complete</label>
      <ul className="todo-list">
        {/* todolist的每一项 */}
        **{todos.map((item) => {
          return <TodoItem key={item.id} todos={item}></TodoItem>
        })}**
      </ul>
    </section>
  )
  1. TodoItem.jsx子组件中接收每一项。并渲染

    1. 划线样式类名:completed
    2. 展示输入框类名:editing
    export default function TodoItem(**props**) {
      const todoitem = props.todos
      return (
        // completed - 划线,已完成事项
        // editing - 输入事项
        <li className={todoitem.done ? 'completed' : ''}>
          <div className="view">
             {/* 复选框设置选中状态 */}
            <input className="toggle" type="checkbox" checked={todoitem.isDone} />
            <label>{todoitem.name}</label>
            <button className="destroy"></button>
          </div>
          <input className="edit" />
        </li>
      )
    }
    
    

在这里插入图片描述

做到这里,我们会发现控制台报错:

在这里插入图片描述
意思是我们这里添加了checked属性,但是需要添加一个change事件。所以接下来需要添加change事件。

修改单项🐣

添加事件🐥

因为当前是受控组件,无法修改。所以需要给他一个onChange事件

onChange事件交给store去修改数据。


思路:

  1. 绑定onChange事件,在这个事件中用dispatch触发action行为
  2. 定义一个action行为
  3. 声明actionTypes
  4. 根据行为在todosReducer里面处理状态

代码:

  1. 绑定onChange事件

    1. 传递id和当前状态
    <input
      className="toggle"
      type="checkbox"
      checked={todoitem.isDone}
      onChange={() => {
        dispatch(changeDone(todoitem.id, !todoitem.isDone))
      }}
    />
    
  2. 定义action行为

    import { CHANGE_STATE } from '../constants/todo'
    
    // 修改单个状态的行为
    export const changeDone = (id) => {
      return {
        type: CHANGE_STATE,
        id,
      }
    }
    
    
  3. 声明actionType

    // 声明 constantTypes
    export const CHANGE_STATE = 'todos/changeDone' // 修改单个复选框状态类型
    
    
  4. todosReducer里面处理状态

    case CHANGE_STATE:
          // 注意:状态不可变
          return state.map((item) => {
            if (item.id === action.id) {
              return {
                ...item,
                isDone: action.isDone,
              }
            } else {
              return item
            }
          })
    
  5. 使用dispatch触发action

    import React from 'react'
    import { useDispatch } from 'react-redux'
    ...
    export default function TodoItem(props) {
     ...
      const dispatch = useDispatch()
      return (
    	  ...
            <input
              className="toggle"
              type="checkbox"
              checked={todoitem.isDone}
              onChange={() => {
                **dispatch**(changeDone(todoitem.id, !todoitem.isDone))
              }}
            />
         ...
      )
    }
    
    

删除单项🐤

思路:

  1. 给X绑定点击事件 onClick
  2. 定义一个action行为
  3. 声明actionTypes
  4. 根据行为在todosReducer里面处理状态

代码:

  1. 给X绑定点击事件 onClick

    <button
    className="destroy"
    onClick={() => {
      dispatch(delTodo(todoitem.id))
    }}
    ></button>
    
  2. 定义一个action行为

    // 删除单个代办项
    export const delTodo = (id) => {
      return {
        type: DELETE_TODO,
        id,
      }
    }
    
    
  3. 声明actionTypes

    export const DELETE_TODO = 'todos/delTodo' // 删除单个待办
    
  4. 根据行为在todosReducer里面处理状态

      case DELETE_TODO:
          return state.filter((item) => {
            // 过滤掉与选择的这一行相同的id
            return item.id !== action.id
          })
    

    添加单项🦜

    1. 绑定onChange事件,得到输入框的输入内容

      import React, { useState } from 'react'
      import { useDispatch } from 'react-redux'
      import { addTodo } from '../store/actions/todo'
      
      export default function TodoHeader() {
        **const [inputValue, setInputValue] = useState('')
      
        // 添加单项todo
        const addValue = (e) => {
          setInputValue(e.target.value)
        }**
        return (
          <header className="header">
            <h1>todos</h1>
            <input
              className="new-todo"
              placeholder="今天做什么?"
              value={inputValue}
              autoFocus
              **onChange={addValue}**
            />
          </header>
        )
      }
      
      
    2. 绑定onKeyDown 事件,键盘按下时传递输入项value

        <input
              className="new-todo"
              placeholder="今天做什么?"
              value={inputValue}
              autoFocus
              onChange={addValue}
              onKeyDown={(e) => {
                if (e.key === 'Enter') {
                  console.log('回车', inputValue)
                  dispatch(addTodo(inputValue))
                  setInputValue('') // 清空输入框
                }
              }}
            />
      
    3. 定义一个action行为

      // 添加单个待办项
      export const addTodo = (inputValue) => {
        return {
          type: ADD_TODO,
          name: inputValue,
        }
      }
      
    4. 声明actionTypes

      export const ADD_TODO = 'todos/addTodo' // 添加单个待办项
      
    5. 根据行为在todosReducer里面处理状态

      case ADD_TODO:
            if (!action.name.trim()) return
            // 状态不可变!!!
            return [
              {
                id: state.length + 1,
                name: action.name,
                isDone: false,
              },
              ...state,
            ]
      

底部筛选🐩

在这里插入图片描述

<aside>
💡 要实现底部筛选,可以在footer中使用过滤器进行分发。

</aside>

一、列表项绑定筛选后数据

  1. 声明actionTypes

    // 筛选栏标题
    export const SHOW_ALL = 'show_all'
    export const SHOW_COMPLETED = 'show_completed'
    export const SHOW_ACTIVE = 'show_active'
    // 筛选行为
    export const SET_VISIBILITY_FILTER = 'todos/setVisibilityFilter'
    
  2. 定义筛选栏标签的静态数据

    import { SHOW_ALL,SHOW_ACTIVE,SHOW_COMPLETED } from "./todo";
    
    export  const FILTER_TITLES = {
        [SHOW_ALL]: 'All',
        [SHOW_ACTIVE]: 'Active',
        [SHOW_COMPLETED]: 'Completed'
      }
    
  3. 定义一个action行为

    // 底部筛选栏 - 用于更新Redux store中的过滤状态
    export const setVisibilityFilter = (filter) => ({
      type: SET_VISIBILITY_FILTER,
      filter
    })
    
  4. 根据行为在todosReducer里面处理状态

    1. 新建一个reducer/filter.js
    import { SET_VISIBILITY_FILTER } from '../constants/todo'
    import { SHOW_ALL } from '../constants/todo'
    // 设置已完成&未完成,并返回参数。
    const visibilityFilter = (state = SHOW_ALL, action) => {
      switch (action.type) {
        case SET_VISIBILITY_FILTER:
          return action.filter
        default:
          return state
      }
    }
    
    export default visibilityFilter
    
    1. 新建一个selector/isVisible.js
    // todo项是否可见 方法
    import { SHOW_ACTIVE, SHOW_ALL, SHOW_COMPLETED } from '../constants/todo'
    
    export function selectVisible(state = [], filter) {
      switch (filter) {
        case SHOW_ALL:
          return state
        case SHOW_ACTIVE:
          return state.filter((todo) => !todo.isDone)
        case SHOW_COMPLETED:
          return state.filter((todo) => todo.isDone)
        default:
          return state
      }
    }
    
  5. TodoMain.jsx中,使用筛选(未完成/已完成/全部)后的状态来循环渲染列表项

// 筛选出已完成or未完成or全部的项
// 传入两个参数-参数1:所有数据;参数2:过滤条件
  const visibleTodos = useSelector((state) =>
    selectVisible(state.todos, state.visibilityFilter)
  )

二、底部筛选栏设置过滤条件

  1. TodoFooter.jsx中,循环渲染过滤条件。
  2. 给a链接绑定onClick事件,触发action行为。实现数据的过滤展示。
    <ul className="filters">
      {Object.keys(FILTER_TITLES).map((filterTitle) => (
        <li key={filterTitle}>
          <a
            href="./#"
            className={classNames({ selected: filterTitle === filter })}
            onClick={() => dispatch(setVisibilityFilter(filterTitle))}
          >
            {FILTER_TITLES[filterTitle]}
          </a>
        </li>
      ))}
    </ul>
    

删除全部已完成☘️

  1. 给按钮绑定点击事件 onClick

    <button
      className="clear-completed"
      onClick={() => dispatch(changeAll(true))}
    >
      Clear completed
    </button>
    
  2. 定义一个action行为

    // 清除所有已完成
    export const changeAll = (isDone) => {
      return {
        type: CHANGE_ALL,
        isDone,
      }
    }
    
  3. 声明actionTypes

    export const CHANGE_ALL = 'todos/changeAll' // 清除所有已完成
    
  4. 根据行为在todosReducer里面处理状态

    case CHANGE_ALL:
    	return state.filter((item) => {
    	  return item.isDone !== action.isDone
    })
    

持久化存储 - 本地 🌈

在这里插入图片描述

  1. 定义一个action行为

    // 本地localstore存储
    export const setLocalToken = (todos) => ({
      type: SET_LOCAL_TOKEN,
      todos,
    })
    
  2. 声明actionTypes

    // 本地localstore存储
    export const SET_LOCAL_TOKEN = 'todos/setLocalToken'
    
  3. 根据行为在reducer里面处理状态

    case SET_LOCAL_TOKEN:
          return action.todos
    
  4. TodoMain.jsx中触发action

    const todos = useSelector((state) => state.todos)
    // 触发action,传入本地存储的状态
      useEffect(() => {
        const savedTodos = JSON.parse(localStorage.getItem('todos'))
        if (savedTodos) {
          dispatch(setLocalToken(savedTodos))
        }
    //[dispatch] 作为依赖数组。只有当 dispatch 更新时才重新执行 useEffect 中的逻辑
      }, [dispatch])
    // 状态存储到本地
      useEffect(() => {
        localStorage.setItem('todos', JSON.stringify(todos))
      }, [todos])
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1944190.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

超声波眼镜清洗机什么牌子好?入门级家用超声波清洗机推荐

戴眼镜的人一定都有对眼镜店的超声波清洗机清洗过的眼镜惊讶过&#xff0c;洗过之后光洁如新&#xff0c;镜片清澈透亮。最初笔者以为超声波清洗机只能用来清洗眼镜&#xff0c;没想到它还能清洗很多小玩意儿。目前市面上的超声波清洗机品牌繁多&#xff0c;今天笔者来教教大家…

QtCreator和QtDesignStudio最佳实践

一、QTC和QDS工作流概述 很多初学者对 QDS(Qt Design Studio) 和 QTC(Qt Creator)如何配合经常存有疑问&#xff0c;本文介绍具体的工作流程。 工作流程 1.产品设计&#xff1a;通过PS、Figma、XD等专业工具设计页面视觉和原型。 2.QDS 原型制作&#xff1a;导入设计源文件、…

计算机网络-配置双机三层互联(静态路由方式)

目录 交换机工作原理路由器工作原理路由信息表组成部分路由器发决策 ARP工作原理配置双机三层互联&#xff08;静态路由方式&#xff09; 交换机工作原理 MAC自学习过程 初始状态&#xff1a; 刚启动的交换机的MAC地址表是空的。 学习过程&#xff1a; 当交换机收到一个数据帧…

【QML之·组件】

系列文章目录 文章目录 前言一、概述2.QML组件的重要性 二、实例演示总结 前言 组件是QML中的一个重要概念&#xff0c;它是用户界面的构建块。组件是可重用的&#xff0c;可以在不同的界面中使用。每个组件都有自己的属性、信号和方法&#xff0c;可以通过绑定和事件处理来实现…

C# 与C++ cli

cli CLI&#xff08;Command Line Interface&#xff09;是一种通过命令行界面与计算机系统进行交互的方式。它提供了一种以文本形式输入命令和接收系统输出的方法&#xff0c;用于执行各种操作和管理计算机系统。以下是CLI的详细解释&#xff1a; 一、定义与基本概念 定义&…

时间序列数据增强方法概述

时间序列数据增强方法概述 时间序列数据增强是一种提高模型泛化能力和预测准确性的技术&#xff0c;通过在原始数据集上生成新的样本&#xff0c;可以增加模型训练过程中的多样性和鲁棒性。本文将介绍几种常用的时间序列数据增强方法&#xff0c;并提供相应的Python代码示例。…

OS Copilot初体验的感受与心得

本文介绍体验操作系统智能助手OS Copilot后&#xff0c;个人的一些收获、体验等。 最近&#xff0c;抽空体验了阿里云的操作系统智能助手OS Copilot&#xff0c;在这里记录一下心得与收获。总体观之&#xff0c;从个人角度来说&#xff0c;感觉这个OS Copilot确实抓住了不少开发…

宝塔国际版Docker Manager 3.4获取镜像列表报错解决办法

宝塔国际版安装Docker Manager 3.4,遇到获取镜像列表的时候报错。 解决办法 找到:/www/server/panel/plugin/docker/docker_main.py文件 替换函数utc_to_local 原代码 # UTC时间转换为时间戳def utc_to_local(self, utc_time_str, utc_format=%Y-%m-%dT%H:%M:%S):

邮件安全篇:如何防止邮件泄密?

本文主要讨论组织内部用户违反保密规定通过邮件泄密的场景。其他场景导致邮箱泄密的问题&#xff08;如账号被盗、邮件系统存在安全漏洞等&#xff09;不在本文的讨论范围。本文主要从邮件系架构设计、邮件数据防泄漏系统、建立健全规章制度、安全意识培训等方面分别探讨。 1. …

SpringBoot整合Spring Boot Admin实现监控

目录 基本操作流程&#xff1a; 服务端 server 0.创建一个springboot项目 1.导入依赖 2.添加配置信息 3.在启动类添加注解 4.运行 客户端client 1.添加依赖 2.添加配置信息 3.运行 基本操作流程&#xff1a; 服务端 server 0.创建一个springboot项目 1.导入依赖 …

Wordpress安装到win10(2024年7月)

目录 1.wordpress介绍 2下载应用 2.1.wordpress 2.2XAMPP 2.3 PHPmyadmin 3.配置应用 3.1XAMPP进程 3.2 文件配置 3.3 phpmyadmin配置 4.配置网页 4.1 数据库创建 4.2 安装wordpress 5.进入面板 6.总结 1.wordpress介绍 WordPress是一个开源内容管理系统&#xff0…

域名解析到ipv6,并用CF隐藏端口

要求&#xff1a;域名解析到 IPv6 地址并隐藏端口 ‍ 效果&#xff1a;用域名 https://myhalo.soulio.top​ 访问http://[2409:8a62:867:4f12:56c7:5508:f7x6:8]:8080​。唯一缺点是延迟有点高。 ​​ ‍ 难度&#xff1a;需要有一定域名解析、cloudflare使用基础 ‍ 实…

深度学习实战笔记2实战Kaggle比赛:预测房价

此数据集由Bart de Cock于2011年收集 :cite:De-Cock.2011&#xff0c; 涵盖了2006-2010年期间亚利桑那州埃姆斯市的房价。 这个数据集是相当通用的&#xff0c;不会需要使用复杂模型架构。 它比哈里森和鲁宾菲尔德的波士顿房价 数据集要大得多&#xff0c;也有更多的特征。 1下…

Linux云计算 |【第一阶段】SERVICES-DAY6

主要内容&#xff1a; Linux容器基础、Linux容器管理、podman命令行、管理容器进阶 实操前骤&#xff1a;安装 RHEL8.2 虚拟机 1.选择软件包&#xff1a;rhel-8.2-x86-dvd.iso&#xff1b; 2.内存2048M&#xff1b; 3.时区选择亚洲-上海&#xff0c;带GUI的服务器&#xff1b…

Element-ui :el-table 中表尾合计行

Table 表格 | Element Plus <template><el-table :data"tableData" border show-summary :summary-method"getSummariesss" style"width: 100%"><el-table-column prop"id" label"ID" width"180"…

Postman设置全部请求都携带请求头,Postman如何一次性设置请求头、不需要一个请求一个请求去添加请求头

文章目录 一、问题描述二、解决办法三、应用场景 一、问题描述 现在我有 n 个接口测试&#xff0c;其中 n 个都需要携带一致的请求头&#xff08;其实一般都是携带 JWT 令牌&#xff09;&#xff0c;怎么办&#xff1f;我要一个一个写&#xff1f;如图&#xff1a; 二、解决办…

mac M1安装换脸Roop教程及所遇到的问题

1.安装miniconda&#xff0c;下载地址&#xff1a; 按 Python 版本划分的最新 Miniconda 安装程序链接&#xff1a;https://docs.anaconda.com/miniconda/miniconda-other-installer-links/ 下载后直接默认安装即可。 我用的是&#xff1a;Python3.10对应的Miniconda 2.下载…

vCenter 错误提示 “目标主机上的vmotion接口未配置”

vCenter 错误提示 “目标主机上的vmotion接口未配置” VMware 使用 vCenter 迁移 虚拟机报错 “目标主机上的 vMotion 接口未配置”&#xff0c;配置启用 vMotion 的步骤如下&#xff1a; &#xff08;END&#xff09;

【机器学习】不同操作系统下如何安装Jupyter Notebook和Anaconda

引言 Jupyter Notebook 是一个非常流行的开源Web应用程序&#xff0c;允许你创建和共享包含代码、方程、可视化和解释性文本的文档 文章目录 引言一、如何安装Jupyter Notebook1.1 对于Windows用户1.2 对于macOS用户1.3 对于Linux用户&#xff1a; 二、如何安装Anaconda2.1 对于…

mac清理软件哪个好用免费 MacBook电脑清理软件推荐 怎么清理mac

随着使用时间的增长&#xff0c;mac电脑会积累一些不必要的垃圾文件&#xff0c;这些文件会占用宝贵的存储空间&#xff0c;影响电脑的运行速度和稳定性。因此&#xff0c;定期清理mac电脑的垃圾文件是非常有必要的。市场上有许多优秀的Mac清理软件&#xff0c;包括一些出色的国…