前段时间实现了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代码
- 新建store的目录结构如下所示
export const ADD_TODO = 'add_todo' // 增
export const DELETE_TODO = 'delete_todo' // 删
export const COMPLETE_TODO = 'complete_todo' // 改
export const SEARCH_TODO = 'search_todo' // 查
复制代码
- 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
}
}
复制代码
- 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
复制代码
- 最后修改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
- 安装react-redux
yarn add react-redux
复制代码
- 修改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,一般表示为函数式组件。
- 修改项目结构如下所示
- 修改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
复制代码
- 修改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
复制代码
-
其他组件也按照上面展示组件和容器组件进行拆分详情查看项目
-
修改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;
复制代码