组件拆分
- src/components/Header.js ```jsx import React, { Component } from ‘react’
class Header extends Component { render() { return (
todos
export default Header
- src/components/Main.js```jsximport React, { Component } from 'react'class Main extends Component {render() {return (<section className="main"><input className="toggle-all" type="checkbox" /><ul className="todo-list"><li className="completed"><div className="view"><input className="toggle" type="checkbox"/><label>品味JavaScript</label><button className="destroy"></button></div><input className="edit" /></li><li><div className="view"><input className="toggle" type="checkbox" /><label>买一个宠物</label><button className="destroy"></button></div><input className="edit" /></li></ul></section>)}}export default Main
- src/components/Footer.js ```jsx import React, { Component } from “react”;
class Footer extends Component { render() { return (
); } }export default Footer;
- src/App.js```jsximport React, { Component } from 'react'import Header from './components/Header'import Main from './components/Main'import Footer from './components/Footer'class App extends Component {render() {return (<div className="todoapp"><Header /><Main /><Footer /></div>)}}export default App
- src/index.js ```jsx import React from ‘react’; import ReactDOM from ‘react-dom’; import App from ‘./App’ import ‘./index.css’
ReactDOM.render(
<a name="FAp1B"></a>## 任务列表展示- src/store/actions/todo.actions.js```javascript/*** 01 发送请求获取数据 load_todo* 02 当异步操作完成之后还需要触发新的指令 load_todo_success*/import { createAction } from 'redux-actions'export const load_todo = createAction('load_todo')export const load_todo_success = createAction('load_todo_success')
- src/store/reducers/todo.reducer.js
```javascript
/**
- 接收指令后对数据进行相应处理 */ import { handleActions as createReducer } from ‘redux-actions’ import {load_todo_success } from ‘../actions/todo.actions’
const initialState = { todos: [] }
export default createReducer({ [load_todo_success]: (state, action) => ({ todos: action.payload }) }, initialState)
- src/store/reducers/root.reducer.js```javascriptimport { combineReducers } from 'redux'import todoReducer from './todo.reducer'export default combineReducers({todoReducer: todoReducer})
- src/store/saga/todo.sags.js
```javascript
/**
- 01 完成异步操作
- 02 重新发送新的指令 */ import axios from ‘axios’ import { takeEvery, put } from ‘redux-saga/effects’ import { load_todo_success, load_todo } from ‘../actions/todo.actions’
// 实现 load_todo_data 获取数据同时传递新指令 function * load_todo_data() { let todoData = yield axios.get(‘http://localhost:3005/api/todos').then(res => res.data) console.log(todoData) yield put(load_todo_success(todoData)) }
export default function * todoSaga() { yield takeEvery(load_todo, load_todo_data) }
- src/store/index.js```javascript/*** 当前模块完成 store 的创建 + 中间件注册*/import { createStore, applyMiddleware } from 'redux'import rootReducer from './reducers/root.reducer'import createSagaMiddleware from 'redux-saga'import todoSaga from './saga/todo.saga'const sagaMiddleware = createSagaMiddleware()const store = createStore(rootReducer, applyMiddleware(sagaMiddleware))sagaMiddleware.run(todoSaga)export default store
- src/index.js ```jsx
import store from ‘./store’ import {Provider} from ‘react-redux’
ReactDOM.render(
- src/components/Main.js```jsximport React, { Component } from 'react'import {connect} from 'react-redux'import { bindActionCreators} from 'redux'import * as todoActions from '../store/actions/todo.actions'class Main extends Component {componentDidMount() {// 调用具体的指令函数,触发数据获取操作this.props.load_todo()}render() {console.log(this.props)return (<section className="main"><input className="toggle-all" type="checkbox" /><ul className="todo-list">{this.props.todos.map(item => (<li key={item.id}><div className="view"><input className="toggle" type="checkbox" /><label>{item.taskName}</label><button className="destroy"></button></div><input className="edit" /></li>))}</ul></section>)}}// 获取 store 当中数据const mapStateToProps = (state) => ({todos: state.todoReducer.todos})// 处理 dispatchconst mapDispatchToProps = (dispatch) => ({...bindActionCreators(todoActions, dispatch)})export default connect(mapStateToProps, mapDispatchToProps)(Main)
实现添加任务
src/store/actions/todo.actions.js
// 新增 todo 列表项的相关指令export const add_todo = createAction('add_todo')export const add_todo_success = createAction('add_todo_success')
src/store/reducers/todo.reducer.js ```javascript
import { add_todo_success } from ‘../actions/todo.actions’
export default createReducer({
[add_todo_success]: (state, action) => ({ todos: [ …state.todos, action.payload ] }) }, initialState)
- src/store/saga/todo.saga.js```javascriptimport { add_todo_success, add_todo } from '../actions/todo.actions'// 实现 add_todo_data 方法function * add_todo_data (action) {// 完成异步操作let taskInfo = yield axios.post('http://localhost:3005/api/todos', {taskName: action.payload}).then(res => res.data)// 重新发送指令yield put(add_todo_success(taskInfo.task))}export default function * todoSaga() {yield takeEvery(add_todo, add_todo_data)}
- src/components/Header.js ```jsx import React, { Component } from ‘react’ import {connect} from ‘react-redux’ import {bindActionCreators} from ‘redux’ import * as todoActions from ‘../store/actions/todo.actions’
class Header extends Component {
addTodo = (ev) => { if(ev.keyCode === 13) { // 获取当前输入框里的内容 let taskName = ev.target.value if(taskName.trim().length === 0){ alert(‘请输入任务内容’) return }
// 触发新增任务的指令this.props.add_todo(taskName)// 清空文本框ev.target.value = ''}
} render() { return (
Todos
// 组件与store const mapStateToProps = (state) => ({ todos: state.todoReducer.todos })
// 组件与dispatch const mapDispatchToProps = (dispatch) => ({ …bindActionCreators(todoActions, dispatch) })
export default connect(mapStateToProps, mapDispatchToProps)(Header)
<a name="EeMMW"></a>## 删除任务- src/store/actions/todo.actions.js```javascript// 删除指令export const remove_todo = createAction('remove_todo')export const remove_todo_success = createAction('remove_todo_success')
- src/store/reducers/todo.reducer.js ```javascript
import { remove_todo_success } from ‘../actions/todo.actions’
export default createReducer({
[remove_todo_success]: (state, action) => { // 需要获取被删除项的 id let id = action.payload
let index = state.todos.findIndex(todo => todo.id === id)let todos = JSON.parse(JSON.stringify(state.todos))todos.splice(index, 1)return { todos }
} }, initialState)
- src/store/saga/todo.saga.js```javascriptimport { remove_todo_success, remove_todo } from '../actions/todo.actions'// 实现 remove_todo_datafunction * remove_todo_data(action) {// 将 id 利用相应的接口传递给后端执行具体的删除操作let res = yield axios.delete('http://localhost:3005/api/todos', {params: {id: action.payload}}).then(res => res.data)// 重新发送指令yield put(remove_todo_success(res.tasks.id))}export default function * todoSaga() {yield takeEvery(remove_todo, remove_todo_data)}
- src/components/Main.js ```jsx
class Main extends Component {
removeTask(id){ if(window.confirm(‘确定删除当前任务’)){ // 触发删除操作 this.props.remove_todo(id) } }
render() { console.log(this.props) return (
-
{
this.props.todos.map(item => (
-
<a name="o9Mc0"></a>## 修改任务状态- src/store/actions/todo.actions.js```javascript// 修改任务状态export const modify_todo = createAction('modify_todo')export const modify_todo_success = createAction('modify_todo_success')
- src/store/reducers/todo.reducer.js ```javascript import { modify_todo_success } from ‘../actions/todo.actions’
export default createReducer({
[modify_todo_success]: (state, action) => { let params = action.payload
let index = state.todos.findIndex(todo => todo.id === params.id)let todos = JSON.parse(JSON.stringify(state.todos))todos[index].isCompleted = params.isCompletedreturn { todos }
} }, initialState)
- src/store/saga/todo.saga.js```javascriptimport { modify_todo_success, modify_todo } from '../actions/todo.actions'// 实现 modify_todofunction * modify_todo_data(action) {let params = action.payloadyield axios.put('http://localhost:3005/api/todos/isCompleted', params).then(res => res.data)yield put(modify_todo_success(params))}export default function * todoSaga() {yield takeEvery(modify_todo, modify_todo_data)}
- src/components/Main.js ```jsx
class Main extends Component {
render() { console.log(this.props) return (
-
{
this.props.todos.map(item => (
-
{this.props.modify_todo({id: item.id, isCompleted: ev.target.checked})}}/>
))
}
<a name="dRCoL"></a>## 计算未完成功能- src/components/Footer.js```jsximport React, { Component } from "react";import { connect } from 'react-redux'import { bindActionCreators } from 'redux'import * as todoActions from '../store/actions/todo.actions'class Footer extends Component {render() {// 此时我们就可以过虑出未选中的任务,然后统计它们的数据,做为具体的值使用let taskLen = this.props.todos.filter(todo => !todo.isCompleted).lengthreturn (<footer className="footer"><span className="todo-count"><strong>{taskLen}</strong> item left</span><ul className="filters"><li><span>All</span></li><li><span>Active</span></li><li><span>Completed</span></li></ul><button className="clear-completed">Clear completed</button></footer>);}}// 获取 store 当中数据const mapStateToProps = (state) => ({todos: state.todoReducer.todos})// 处理 dispatch 函数const mapDispatchToProps = (dispatch) => ({...bindActionCreators(todoActions, dispatch)})export default connect(mapStateToProps, mapDispatchToProps)(Footer)
任务筛选功能
src/store/actions/todo.actions.js
// 任务筛选export const modify_todo_filter = createAction('modify_todo_filter')
src/store/reducers/todo.reducer.js ```javascript /**
- 接收指令后对数据进行相应处理 */ import { handleActions as createReducer } from ‘redux-actions’ import {load_todo_success, add_todo_success, remove_todo_success, modify_todo_success, modify_todo_filter } from ‘../actions/todo.actions’
const initialState = { todos: [], filter: ‘all’ }
export default createReducer({ [load_todo_success]: (state, action) => ({ …state, todos: action.payload }),
[add_todo_success]: (state, action) => ({ …state, todos: [ …state.todos, action.payload ] }),
[remove_todo_success]: (state, action) => { // 需要获取被删除项的 id let id = action.payload
let index = state.todos.findIndex(todo => todo.id === id)let todos = JSON.parse(JSON.stringify(state.todos))todos.splice(index, 1)return { ...state, todos }
},
[modify_todo_success]: (state, action) => { let params = action.payload let index = state.todos.findIndex(todo => todo.id === params.id) let todos = JSON.parse(JSON.stringify(state.todos)) todos[index].isCompleted = params.isCompleted return {…state, todos } },
[modify_todo_filter]: (state, action) => { return { …state, filter: action.payload } } }, initialState)
- src/components/Footer.js```jsx<ul className="filters"><li><span onClick={() => {this.props.modify_todo_filter('all')}}>All</span></li><li><span onClick={() => {this.props.modify_todo_filter('active')}}>Active</span></li><li><span onClick={() => {this.props.modify_todo_filter('completed')}}>Completed</span></li></ul>
- src/components/Main.js
```jsx
// 依据 filter 筛选出需要展示的数据
function filterTodos(todos, filter) {
switch(filter) {
case ‘all’:
case ‘active’:return todos
case ‘completed’:return todos.filter(todo => !todo.isCompleted)
} }return todos.filter(todo => todo.isCompleted)
// 获取 store 当中数据 const mapStateToProps = (state) => ({ todos: filterTodos(state.todoReducer.todos, state.todoReducer.filter) })
<a name="uu8lf"></a>## 删除已完成任务- src/store/actions/todo.actions.js```javascript// 删除已完成任务export const clear_todo_completed = createAction('clear_todo_completed')export const clear_todo_completed_success = createAction('clear_todo_completed_success')
- src/store/reducers/todo.reducer.js ```javascript
import { clear_todo_completed_success } from ‘../actions/todo.actions’
export default createReducer({
[clear_todo_completed_success]: (state, action) => { let todos = JSON.parse(JSON.stringify(state.todos)) todos = todos.filter(todo => !todo.isCompleted) return { …state, todos } } }, initialState)
- src/store/saga/todo.saga.js```javascriptimport { clear_todo_completed_success, clear_todo_completed } from '../actions/todo.actions'// 实现 clear_todo_datafunction * clear_todo_data() {// 发送删除请求yield axios.delete('http://localhost:3005/api/todos/clearCompleted')yield put(clear_todo_completed_success())}export default function * todoSaga() {yield takeEvery(clear_todo_completed, clear_todo_data)}
- src/components/Footer.js ```jsx
class Footer extends Component {
clear_todo = () => { if(window.confirm(‘是否删除已完成任务’)) { this.props.clear_todo_completed() } } render() {
return (<footer className="footer"><button className="clear-completed" onClick={this.clear_todo}>Clear completed</button></footer>);
} }
<a name="oXNnL"></a>## 修改任务名称(一)- src/store/actions/todo.actions.js```javascript// 任务名称修改export const modify_todo_edit = createAction('modify_todo_edit')export const modify_todo_edit_success = createAction('modify_todo_edit_success')
- src/store/reducers/todo.reducer.js ```javascript import { modify_todo_edit_success } from ‘../actions/todo.actions’
export default createReducer({
[modify_todo_edit_success]: (state, action) => { // 利用 id 找到需要被操作的任务项,设置 isEditing 显示具体的样式 let todos = JSON.parse(JSON.stringify(state.todos)) let index = state.todos.findIndex(todo => todo.id === action.payload.id) todos[index].isEditing = action.payload.isEditing return { …state, todos } } }, initialState)
- src/store/saga/todo.saga.js```javascriptimport { modify_todo_edit_success, modify_todo_edit } from '../actions/todo.actions'// 实现 modify_todo_edit_datafunction * modify_todo_edit_data (action) {yield axios.put('http://localhost:3005/api/todos/isEditing', action.payload)yield put(modify_todo_edit_success(action.payload))}export default function * todoSaga() {yield takeEvery(modify_todo_edit, modify_todo_edit_data)}
- src/components/Main.js
this.props.todos.map(item => {let classes = []if (item.isCompleted) classes.push('completed')if (item.isEditing) classes.push('editing')return (<li key={item.id} className={classes.join(' ')}><div className="view"><inputclassName="toggle"type="checkbox"defaultChecked={item.isCompleted}onChange={(ev) => {this.props.modify_todo({id: item.id, isCompleted: ev.target.checked})}} /><labelonDoubleClick = {() => {this.props.modify_todo_edit({id: item.id, isEditing: true})}}>{item.taskName}</label><button className="destroy" onClick={() => {this.removeTask(item.id)}}></button></div><input defaultValue={item.taskName} className="edit" /></li>)})
修改任务名称(二)
src/store/actions/todo.actions.js
// 任务名称修改确认export const modify_todo_name = createAction('modify_todo_name')export const modify_todo_name_success = createAction('modify_todo_name_success')
src/store/reducers/todo.reducer.js ```javascript import { modify_todo_name_success } from ‘../actions/todo.actions’
export default createReducer({
[modify_todo_name_success]: (state, action) => { let todos = JSON.parse(JSON.stringify(state.todos)) let index = state.todos.findIndex(todo => todo.id === action.payload.id) todos[index].taskName = action.payload.taskName return { …state, todos } } }, initialState)
- src/store/saga/todo.saga.js```javascriptimport { modify_todo_name_success, modify_todo_name } from '../actions/todo.actions'// 实现 modify_todo_name_datafunction * modify_todo_name_data(action) {yield axios.put('http://localhost:3005/api/todos', action.payload)yield put(modify_todo_name_success(action.payload))}export default function * todoSaga() {yield takeEvery(modify_todo_name, modify_todo_name_data)}
- src/components/Main.js ```jsx class Main extends Component {
modify_name(id, ev) { // 切换状态 this.props.modify_todo_edit({id: id, isEditing: false}) // 修改数据 this.props.modify_todo_name({id: id, taskName: ev.target.value}) }
render() { return (
-
{
this.props.todos.map(item => {
let classes = []
if (item.isCompleted) classes.push(‘completed’)
if (item.isEditing) classes.push(‘editing’)
return (
-
{this.props.modify_todo({id: item.id, isCompleted: ev.target.checked})}} />
