React 实现一个简单的待办事项列表 | 青训营

134 阅读3分钟

之前学了Vue,最近在学React,其实上手比较快的,感觉框架都有相通的地方。刚刚学了Mobx和Redux两种状态管理工具,感觉分别跟vuex和pinia有点像。

前置知识

  • JSX语法
    • 列表渲染
    • 变量动态绑定
  • 状态管理工具
    • Redux
    • Mobx

状态管理

Mobx

使用更方便,使用class定义store,就是要注意在导出使用的组件时记得使用observer()监听组件,才能更新渲染。

基本使用

  1. 定义store
  • 定义数据状态state
  • 在构造器中实现数据响应式处理 makeAutoObservble
  • 定义修改数据的函数action
  • 实例化store并导出
import { makeAutoObservable } from 'mobx'class CounterStore {
  count = 0 // 定义数据
  constructor() {
    makeAutoObservable(this)  // 响应式处理
  }
  // 定义修改数据的方法
  addCount = () => {
    this.count++
  }
}
​
const counter = new CounterStore()
export default counter
  1. React使用store
  • 在组件中导入counterStore实例对象
  • 在组件中使用counterStore实例对象中的数据
  • 通过事件调用修改数据的方法修改store中的数据
  • 让组件响应数据变化
// 导入counterStore
import counterStore from './store'
// 导入observer方法
import { observer } from 'mobx-react-lite'
function App() {
  return (
    <div className="App">
      <button onClick={() => counterStore.addCount()}>
        {counterStore.count}
      </button>
    </div>
  )
}
// 包裹组件让视图响应数据变化
export default observer(App)

Redux

将所有的数据存储在一个单一的存储中,以便在整个应用程序中共享和访问。

基本使用

  1. 创建couterStore
  • 使用toolkit的createSlice方法创建一个独立的子模块
  • 使用configureStore语法组合子模块

子模块:

import { createSlice } from '@reduxjs/toolkit'const counter = createSlice({
  // 模块名称独一无二
  name: 'counter',
  // 初始数据
  initialState: {
    count: 1
  },
  // 修改数据的同步方法
  reducers: {
    add (state) {
      state.count++
    }
  }
})
​
const { add } = counter.actions
const counterReducer = counter.reducer// 导出修改数据的函数
export { add }
// 导出reducer
export default counterReducer

组合子模块:

import { configureStore } from '@reduxjs/toolkit'import counterReducer from './counterStore'export default configureStore({
  reducer: {
    // 注册子模块
    counter: counterReducer
  }
})
  1. 为React提供Redux Store

在入口文件中,渲染根组件的位置通过Provider提供store数据

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
// 导入store
import store from './store'
// 导入store提供组件Provider
import { Provider } from 'react-redux'ReactDOM.createRoot(document.getElementById('root')).render(
  // 提供store数据
  <Provider store={store}>
    <App />
  </Provider>
)
  1. 组件中使用store中的数据

组件使用store中的数据需要借助一个hook方法,叫做useSelector

useSelector(state => state.模块名) 方法的返回值为一个对象,对象中包含store子模块中的所有数据

import { useSelector } from 'react-redux'function App () {
  // 使用数据
  const { count } = useSelector(state => state.counter)
  
  return (
    <div className="App">
      {count}
      <button onClick={clickHandler}>+</button>
    </div>
  )
}
​
export default App
  1. 组件修改store中的数据
  • 使用counterStore模块中导出的add方法创建action对象
  • 通过dispatch函数以action作为参数传入完成数据更新
import { useSelector, useDispatch } from 'react-redux'
import { add } from './store/counterStore'function App () {
  // 使用数据
  const { count } = useSelector(state => state.counter)
  // 修改数据
  const dispatch = useDispatch()
  const clickHandler = () => {
    // 1. 生成action对象
    const action = add()
    // 2. 提交action进行数据更新
    dispatch(action)
  }
  return (
    <div className="App">
      {count}
      <button onClick={clickHandler}>+</button>
    </div>
  )
}
​
export default App
  1. 组件修改数据并传参
  • 通过action的payload属性接收参数
import { createSlice } from "@reduxjs/toolkit"
const counterStore = createSlice({
  name: 'counter', // 独一无二不重复的名字语义化
  // 定义初始化的数据
  initialState: {
    taskList: ['react']
  },
  reducers: {
    // action为一个对象 对象中有一个固定的属性叫做payload 为传递过来的参数
    addTaskList (state, action) {
      state.taskList.push(action.payload)
    }
  }
})
​
// 生成修改数据的方法导出
const { addTaskList } = counterStore.actions
export { addTaskList }
// 生成reducer 导出 供index.js做组合模块
const counterReducer = counterStore.reducerexport default counterReducer
  • dispatch的时候传入实参
<button onClick={() => dispatch(addTaskList('vue'))}>addList</button>

实现功能

  1. 渲染列表

通过列表渲染获取的list数据,根据每个任务不同的isDone状态决定class,使用不同的css样式。

 <ul className="todo-list">
          {
            list.map(item=>(
              <li className={item.isDone ? 'todo completed' : 'todo'} key={item.id}>
              <div className="view">
                <input className="toggle" type="checkbox" value={item.isDone} checked={item.isDone} onClick={()=>{dispatch(changeTask(item.id))}}/>
                <label>{item.name}</label>
                <button className="destroy" onClick={()=>{dispatch(delTask(item.id))}}></button>
              </div>
            </li>
</ul>

其中不同功能的方法由使用的不同状态管理工具决定。

  1. 获取任务列表

Mobx:const {taskStore}=useStore()

Redux:const {list}=useSelector(state=>state.tasks)

  1. 定义增删改查方法

Mobx:

  addTask(name) {
    this.list.push({
      id: uuid(),
      name,
      isDone: false
    })
  }
  changeIsDone(id) {
    const task = this.list.find(item => item.id === id)
    task.isDone = !task.isDone
  }
  deleteTask(id) {
    this.list = this.list.filter(item => item.id !== id)
  }
  toggleAll() {
    const isDone = this.list.every(item => item.isDone)
    this.list.forEach(item => item.isDone = !isDone)
  }

Redux:

  // 修改数据的同步方法
  reducers: {
    // 添加任务
    addTask(state, action) {
        const newTask={
            id:uuid(),
            name:action.payload,
            isDone:false
        }
        state.list.push(newTask)
    },
    // 删除任务
    delTask(state, action) {
        const id = action.payload
        const index = state.list.findIndex(item => item.id === id)
        state.list.splice(index, 1)
    },
    // 修改任务状态
    changeTask(state, action) {
        const id = action.payload
        const task = state.list.find(item => item.id === id)
        task.isDone = !task.isDone
    },
    toggleAll(state) {
        const isDone = state.list.every(item => item.isDone)
        state.list.forEach(item => item.isDone = !isDone)
    },

  }