阅读 212

React + Antd + Redux改进之前简单的todolist

前段时间实现了React + Antd实现简单的todolist,组件之间的传值通过props层层传递太过繁琐,这次改进了项目引入了redux进行状态的管理。项目地址

1. 安装配置Redux

  • 安装redux
yarn add redux
复制代码
  • 配置redux,在根目录下新建文件夹store,在store新建index.js,引入redux的createStore创建简单的store
import { createStore } from "redux";

const reducer = function(state = [], action) {
  return state;
}

const store = createStore(reducer);
复制代码

2. 组织Redux代码

  1. 新建store的目录结构如下所示

2. contants/index.js文件用来定义我们接下来的行为

export const ADD_TODO = 'add_todo' // 增
export const DELETE_TODO = 'delete_todo' // 删
export const COMPLETE_TODO = 'complete_todo' // 改
export const SEARCH_TODO = 'search_todo'  // 查
复制代码
  1. actions/index.js用来定义Action,Action 是把数据从应用传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。这里使用Action创建函数来生成action
import {ADD_TODO, DELETE_TODO, COMPLETE_TODO, SEARCH_TODO} from '../constants'

export function addTodo (todo) {
  return {
    type: ADD_TODO,
    todo
  }
}

export function deleteTodo (id) {
  return {
    type: DELETE_TODO,
    id
  }
}

export function completeTodo (id) {
  return {
    type: COMPLETE_TODO,
    id
  }
}

export function searchTodo (text) {
  return {
    type: SEARCH_TODO,
    text
  }
}
复制代码
  1. reducers/index.js定义reducer,reducer 就是一个纯函数,接收旧的 state 和 action,返回新的 state。指定了应用状态的变化如何响应 actions 并发送到 store 。
import {ADD_TODO, DELETE_TODO, COMPLETE_TODO, SEARCH_TODO} from '../constants'

let initState = [
  {
    id: 1,
    text: 'test123',
    completed: true
  },
  {
    id: 2,
    text: 'testabc',
    completed: false
  },
  {
    id: 3,
    text: 'done123',
    completed: true
  }
]

const todoReducer = (state = initState, action) => {
  switch (action.type) {
    case ADD_TODO:
      return [...state, action.todo]
    case DELETE_TODO: 
      return state.filter(item => {
        return item.id !== action.id
      })
    case COMPLETE_TODO:
      return state.map(item => {
        let obj = action.id === item.id ? {...item, completed: !item.completed} : item
        return obj
      })
    case SEARCH_TODO:
      if (action.text === '') {
        return state
      } else {
        return state.filter(item => {
          return item.content.indexOf(action.text) !== -1
        })
      }
    default:  
      return state
  }
}

export default todoReducer
复制代码
  1. 最后修改store/index.js
import {createStore} from 'redux'
import todoReducer from './reducers'

const store = createStore(todoReducer)

export default store
复制代码

补充:如果有多个reducer,可以这样处理

import { combineReducers } from 'redux';
import reducer1 from './reducers/reducer1'
import reducer2 from './reducers/reducer2'

const allReducers = {
  reducer1,
  reducer2
}

const rootReducer = combineReducers(allReducers);

let store = createStore(rootReducer);
复制代码

4. 安装调试工具

  • 在Chrome中安装redux-devtools-extension插件
  • 在终端安装redux-devtools-extension
yarn add redux-devtools-extension
复制代码
  • 安装完成后修改store/index.js
import {createStore} from 'redux'
import todoReducer from './reducers'
import {composeWithDevTools } from 'redux-devtools-extension'

const store = createStore(todoReducer, composeWithDevTools())

export default store
复制代码

5. 测试redux

  • 修改App.js,引入redux进行测试,store.subscribe注册事件监听当store.dispatch时可以通过store.getState()显示出当前的state
import React from 'react';
import TodoList from './TodoList'
import store from './store'
import {addTodo} from './store/actions'

store.subscribe(() => {
  console.log(store.getState())
})

store.dispatch(addTodo({id:4, text: 'nihao', completed: false}))

function App() {
  return (
    <div className="App">
      <TodoList></TodoList>
    </div>
  );
}

export default App;

复制代码
  • 控制台打印结果如下,可以看到新添加的todo

  • 在redux-devtools插件中也可以观察到状态变化

6. 集成react

  1. 安装react-redux
yarn add react-redux
复制代码
  1. 修改app.js,使用Provider类将React应用程序包装在Redux容器中
import React, { Component } from 'react'
import TodoListApp from './components/TodoListApp'
import {Provider} from 'react-redux'
import store from './store'

class App extends Component {
  render () {
    return(
    <Provider store={store}>
      <TodoListApp/>
    </Provider>     
    )
  }
}

export default App
复制代码

7. 修改项目结构

  • 容器组件和展示组件
    • 容器组件负责与Store交互自身不会触发action,它向展示组件传递由Store获得的props,向Store派发用户操作展示组件引起的action。
    • 展示组件专注于渲染视图是无状态组件,所有数据源于props,一般表示为函数式组件。
  1. 修改项目结构如下所示

containers文件夹存放容器组件,components文件夹存放展示组件,展示组件由function创建无状态组件。

  1. 修改todoItem
  • components/TodoItem.js,此时的TodoItem只是负责展示作用没定义state,数据全部由props传入
import React from 'react';
import { Typography, Button } from 'antd';
import './TodoItem.less'

const { Text } = Typography;

function TodoItem ({completed, id, content, handleChangeItem,handleDeleteItem}) {
  return (
    <div className="item-container" onDoubleClick={() => handleChangeItem(id)} style={{cursor: 'pointer'}}>
      <Text delete={completed}>{content}</Text>
      <Button type="primary" icon="delete" onClick={() => handleDeleteItem(id)}></Button>
    </div>
  )
}

export default TodoItem;
复制代码
  • containers/TodoItems,向展示组件中传入数据
import {connect} from 'react-redux'
import TodoItemComponent from '../components/TodoItem'
import {completeTodo, deleteTodo} from '../store/actions'

const mapDispatchToProps = dispatch => {
  return {
    handleChangeItem: id => {
      dispatch(completeTodo(id))
    },
    handleDeleteItem: id => {
      dispatch(deleteTodo(id))
    }
  }
}
const TodoItemContainer = connect(null, mapDispatchToProps)(TodoItemComponent)

export default TodoItemContainer
复制代码
  1. 修改DataList
  • 展示组件components/DataList
import React from 'react';
import TodoItem from '../containers/TodoItem'
import { List } from 'antd';

function DataList ({list}) {
  return (
    <List
      bordered
      dataSource={list}
      renderItem={item => (
          <List.Item>
            <TodoItem {...item}/>
          </List.Item>
      )}
    />
  )
}

export default DataList;
复制代码
  • 容器组件containers/Datalist
import {connect} from 'react-redux'
import TodoListComponent from '../components/DataList'

const mapStateToProps = state => {
  return {
    list: state
  }
}

const TodoListContainer = connect(mapStateToProps)(TodoListComponent)

export default TodoListContainer
复制代码
  1. 其他组件也按照上面展示组件和容器组件进行拆分详情查看项目

  2. 修改App.js,引入react-redux,被Provider包裹的组件都能拿到store

import React from 'react';
import TodoListApp from './components/TodoListApp'
import store from './store'
import {Provider} from 'react-redux'

function App() {
  return (
    <Provider store={store}>
      <TodoListApp></TodoListApp>
    </Provider>
  );
}

export default App;

复制代码

相关链接:React + Antd实现简单的todolist

React + Antd + Mobx改进之前简单的todolist