前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【案例】使用React+redux实现一个Todomvc

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

作者头像
且陶陶
发布2024-07-25 13:37:07
440
发布2024-07-25 13:37:07
举报
文章被收录于专栏:Triciaの小世界

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📂

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

代码语言:javascript
复制
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
}

store/reducers/index.js 中合并单独的reducer并导出

代码语言:javascript
复制
// 模块合并 并导出
import todos from './todo'
import { combineReducers } from 'redux'

const rootReducer = combineReducers({ todos })
export default rootReducer

store/index.js中挂载 reducer和action

代码语言:javascript
复制
// 创建仓库,挂载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值

代码语言:javascript
复制
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>
)

三、使用仓库状态📉

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

代码语言:javascript
复制
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
代码语言:javascript
复制
 ...
 ...
 
  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>
  )

TodoItem.jsx子组件中接收每一项。并渲染

  1. 划线样式类名:completed
  2. 展示输入框类名:editing
代码语言:javascript
复制
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事件。

修改单项🐣

💡 选择要修改的项目的复选框,然后改变checked状态。

添加事件🐥

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

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


思路:

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

代码:

绑定onChange事件

  1. 传递id和当前状态
代码语言:javascript
复制
<input
  className="toggle"
  type="checkbox"
  checked={todoitem.isDone}
  onChange={() => {
    dispatch(changeDone(todoitem.id, !todoitem.isDone))
  }}
/>

定义action行为

代码语言:javascript
复制
import { CHANGE_STATE } from '../constants/todo'

// 修改单个状态的行为
export const changeDone = (id) => {
  return {
    type: CHANGE_STATE,
    id,
  }
}

声明actionType

代码语言:javascript
复制
// 声明 constantTypes
export const CHANGE_STATE = 'todos/changeDone' // 修改单个复选框状态类型

todosReducer里面处理状态

代码语言:javascript
复制
case CHANGE_STATE:
      // 注意:状态不可变
      return state.map((item) => {
        if (item.id === action.id) {
          return {
            ...item,
            isDone: action.isDone,
          }
        } else {
          return item
        }
      })

使用dispatch触发action

代码语言:javascript
复制
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))
          }}
        />
     ...
  )
}

删除单项🐤

💡 获取要删除的那一项的id,然后过滤掉它。

思路:

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

代码:

给X绑定点击事件 onClick

代码语言:javascript
复制
<button
className="destroy"
onClick={() => {
  dispatch(delTodo(todoitem.id))
}}
></button>

定义一个action行为

代码语言:javascript
复制
// 删除单个代办项
export const delTodo = (id) => {
  return {
    type: DELETE_TODO,
    id,
  }
}

声明actionTypes

代码语言:javascript
复制
export const DELETE_TODO = 'todos/delTodo' // 删除单个待办

根据行为在todosReducer里面处理状态

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

添加单项🦜

💡 首先对拿到的做非空校验;然后数组添加一项数据。

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

代码语言:javascript
复制
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>
  )
}

绑定onKeyDown 事件,键盘按下时传递输入项value

代码语言:javascript
复制
  <input
        className="new-todo"
        placeholder="今天做什么?"
        value={inputValue}
        autoFocus
        onChange={addValue}
        onKeyDown={(e) => {
          if (e.key === 'Enter') {
            console.log('回车', inputValue)
            dispatch(addTodo(inputValue))
            setInputValue('') // 清空输入框
          }
        }}
      />

定义一个action行为

代码语言:javascript
复制
// 添加单个待办项
export const addTodo = (inputValue) => {
  return {
    type: ADD_TODO,
    name: inputValue,
  }
}

声明actionTypes

代码语言:javascript
复制
export const ADD_TODO = 'todos/addTodo' // 添加单个待办项

根据行为在todosReducer里面处理状态

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

底部筛选🐩

代码语言:javascript
复制
<aside>
💡 要实现底部筛选,可以在footer中使用过滤器进行分发。

</aside>
一、列表项绑定筛选后数据

声明actionTypes

代码语言:javascript
复制
// 筛选栏标题
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'

定义筛选栏标签的静态数据

代码语言:javascript
复制
import { SHOW_ALL,SHOW_ACTIVE,SHOW_COMPLETED } from "./todo";

export  const FILTER_TITLES = {
    [SHOW_ALL]: 'All',
    [SHOW_ACTIVE]: 'Active',
    [SHOW_COMPLETED]: 'Completed'
  }

定义一个action行为

代码语言:javascript
复制
// 底部筛选栏 - 用于更新Redux store中的过滤状态
export const setVisibilityFilter = (filter) => ({
  type: SET_VISIBILITY_FILTER,
  filter
})

根据行为在todosReducer里面处理状态

  1. 新建一个reducer/filter.js
代码语言:javascript
复制
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
代码语言:javascript
复制
// 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
  }
}

TodoMain.jsx中,使用筛选(未完成/已完成/全部)后的状态来循环渲染列表项

代码语言:javascript
复制
// 筛选出已完成or未完成or全部的项
// 传入两个参数-参数1:所有数据;参数2:过滤条件
  const visibleTodos = useSelector((state) =>
    selectVisible(state.todos, state.visibilityFilter)
  )
二、底部筛选栏设置过滤条件

TodoFooter.jsx中,循环渲染过滤条件。

给a链接绑定onClick事件,触发action行为。实现数据的过滤展示。

代码语言:javascript
复制
<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>

删除全部已完成☘️

给按钮绑定点击事件 onClick

代码语言:javascript
复制
<button
  className="clear-completed"
  onClick={() => dispatch(changeAll(true))}
>
  Clear completed
</button>

定义一个action行为

代码语言:javascript
复制
// 清除所有已完成
export const changeAll = (isDone) => {
  return {
    type: CHANGE_ALL,
    isDone,
  }
}

声明actionTypes

代码语言:javascript
复制
export const CHANGE_ALL = 'todos/changeAll' // 清除所有已完成

根据行为在todosReducer里面处理状态

代码语言:javascript
复制
case CHANGE_ALL:
	return state.filter((item) => {
	  return item.isDone !== action.isDone
})

持久化存储 - 本地 🌈

💡 将仓库中的状态存储到localStorage中;2. 从浏览器本地存储中得到状态,如果状态存在,仓库中的数据更新为本地存储的数据。

定义一个action行为

代码语言:javascript
复制
// 本地localstore存储
export const setLocalToken = (todos) => ({
  type: SET_LOCAL_TOKEN,
  todos,
})

声明actionTypes

代码语言:javascript
复制
// 本地localstore存储
export const SET_LOCAL_TOKEN = 'todos/setLocalToken'

根据行为在reducer里面处理状态

代码语言:javascript
复制
case SET_LOCAL_TOKEN:
      return action.todos

TodoMain.jsx中触发action

代码语言:javascript
复制
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])
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前情回顾 - 什么是redux 🪷
    • 三个核心概念🌟
    • 🍬TodoMVC案例
    • 功能介绍 🌺
    • 🍿 静态结构
    • 🍰 状态管理 - redux
      • 一、创建store📂
        • 二、引入redux🧊
          • 三、使用仓库状态📉
            • 更改状态🍥
            • 功能实现🍹
              • 界面渲染🕸️
                • 渲染 事项📋
              • 修改单项🐣
                • 添加事件🐥
              • 删除单项🐤
                • 添加单项🦜
                  • 底部筛选🐩
                    • 一、列表项绑定筛选后数据
                    • 二、底部筛选栏设置过滤条件
                  • 删除全部已完成☘️
                    • 持久化存储 - 本地 🌈
                    相关产品与服务
                    对象存储
                    对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档