# my-appto-do-practice **Repository Path**: jlk1912/my-appto-do-practice ## Basic Information - **Project Name**: my-appto-do-practice - **Description**: 以todo为例,配置react全家桶 - **Primary Language**: JavaScript - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 0 - **Created**: 2020-01-31 - **Last Updated**: 2022-05-31 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README **安装** npm install **启动** npm start **概述:** 以to-do为例,编写react全家桶 主要技术: - 框架:react - 语法:es6 - 脚手架:create-react-app - UI:antd - 数据管理:redux - 异步action中间件:redux-thunk - 数据流绑定工具:react-redux - 路由:react-router - 路由绑定工具:react-router-redux - 数据请求工具:axio - 数据模拟工具:YApi # 1、系统设计: 总体模块分为未完成任务和已完成任务 未完成任务中可以添加任务,对已添加的任务标识完成状态,可删除任务 已完成的任务可以标识为未完成,可删除任务 已完成和未完成切换使用路由 # 2、原型设计: 未完成任务: ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/202951_5c200106_717356.png "1576980921399.png") 已完成任务: ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/203030_9c9fb257_717356.png "1576980946079.png") # 3、数据接口设计: ## 3.1、获取任务列表 接口地址如下:http://yapi.demo.qunar.com/mock/63161/getTaskList?type=0 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/203055_208625b7_717356.png "1576984449005.png") # 4、系统开发 ## 4.1 系统初始化 需要先安装node环境,安装create-react-app全局方法 E:\MyProjects> npx create-react-app my-appto-do-practice ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/203109_9c7c415b_717356.png "1576989397775.png") ## 4.2 路由 ### 4.2.1 安装 ``` npm install react-router-dom --save-dev //这里可以使用cnpm代替npm命令 ``` ### 4.2.2 创建子组件 文件路径:src\Page\completeTask.js ```javascript import React from 'react'; export default class CompleteTask extends React.Component { render() { return (
已完成任务内容
) } } ``` 文件路径:src\Page\uncompleteTask.js ```javascript import React from 'react'; export default class UncompleteTask extends React.Component { render() { return (
未完成任务内容
) } } ``` ### 4.2.3 创建模板组件 文件路径:src\Page\MainLayout.js ```javascript import React from 'react'; import Header from './header.js' export default class MainLayout extends React.Component { render() { return (
{this.props.children}
); } } ``` ### 4.2.4 创建头部 文件路径:src\Page\header.js ```javascript import React, { Component } from 'react'; import { Link } from 'react-router-dom'; class Header extends Component { render() { return (

to-do

); } } export default Header; ``` ### 4.2.5 路由文件 文件路径:src\Router.js ```javascript import React from 'react'; import {BrowserRouter, Route, Switch} from 'react-router-dom'; import MainLayout from './Page/MainLayout.js'; import CompleteTask from './Page/completeTask.js'; import UncompleteTask from './Page/uncompleteTask.js'; const BasicRoute = () => { return { const {match} = props; return }/> }/> } }/> }; export default BasicRoute; ``` ​ 使用Switch标签是为了严格匹配,配置中使用了嵌套路由,页面加载哪个组件,与**path全路径相关**。嵌套路由的**关键**是父路由把match.url传给自路由,由子路与match.url拼接成path。 ​ 路由跳转时出现警告:Warning: Failed prop type: Invalid prop 'component' of type 'object' supplied to 'Route', expected 'function'使用下面方法解决: ```javascript }/> ``` ### 4.2.6 配置启动页 ```javascript import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import MainWindow from './Router.js'; import * as serviceWorker from './serviceWorker'; ReactDOM.render(, document.getElementById('root')); serviceWorker.unregister(); ``` ​ ### 4.2.7 结果预览 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/203159_b787424f_717356.png "1577000694331.png") ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/203213_68d623e5_717356.png "1577000729556.png") 参考:https://www.jianshu.com/p/8954e9fb0c7e 参考:https://juejin.im/post/5d467280e51d4561a54b6946 ## 4.3 数据请求 ### 4.3.1 封装 Axios 文件路径:src\axios.js ```javascript import Axios from 'axios'; import qs from 'qs'; const axios = Axios.create(); axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'; axios.interceptors.request.use((config) => { if (config.method === 'post') { config.data = qs.stringify(config.data); } return config; }); // Add a response interceptor axios.interceptors.response.use( (response) => { // Do something with response data let data = response.data; response.data = data.data; return response; }, (error) => { // Do something with response error return Promise.reject(error); } ); export default axios; ``` ### 4.3.2 封装数据请求 文件路径:src\request.js ```javascript import axios from './axios.js'; const rootUrl = 'http://yapi.demo.qunar.com/mock/63161'; export const Tasks = ()=>{ return axios.get(`${rootUrl}/getTaskList?type=0`) } ``` ## 4.4 redux ### 4.4.1 actionType 文件路径:src\constants\actionTypes.js ```javascript /* * action */ export const INIT_TASK_LIST = 'INIT_TASK_LIST';//初始化列表 export const LIST_LOAD = 'LIST_LOAD';//加载完毕 ``` 根据页面设计,定义了重新初始化tasks数组的方法,修改加载状态 ### 4.4.2 reducer 文件路径:src\reducers\tasksReducer.js 默认state: ```javascript const defaultState = { tasks:[], isLoad:false }; ``` 使state变化的方法: ```javascript export default (state = defaultState, action) => { const {type,payload} = action; var tempState = {...state}; switch (type) { case LIST_LOAD: tempState.isLoad = true; return tempState; case INIT_TASK_LIST: tempState.tasks = payload.data return tempState; default: return state; } }; ``` ### 4.4.3 action 路径:src\action\action.js ```javascript const defaultPayload = {} export const loadFinshAction = { type:"LIST_LOAD" } export const initTaskList = (tasks) =>{ console.log("tasks",tasks); defaultPayload.data = tasks; return { type:"INIT_TASK_LIST", payload:defaultPayload } } ``` **loadFinshAction:** 改变加载状态 **initTaskList:** 修改列表 **备注:** payload为传递给reducer的参数,字段内容自定义,在reducer中有对应的接受方法。 ### 4.4.4 异步action 为了使异步请求数据的与dispatch一个action一样,引入了redux-thunk ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/203511_400f20c3_717356.png "1580467024586.png") 配置redux-thunk的步骤如下: 1. 安装redux-thunk 2. 把thunk写入中间件 文件路径:src\index.js ```javascript const store = createStore( Reducers, applyMiddleware(thunk) ); ``` **个人理解:** 异步action的使用场景是为了方便把异步数据加载,页面状态变化合并到一个方法中,方便使用。 如下方的代码,为了使获取数据,初始化数据,修改加载状态成为一个整体,方便使用。 代码路径:src\Page\uncompleteTask.js ```javascript dispatch((dispatch,getState)=>{ request.getTask().then(result=>{ setTimeout(function(){ dispatch(initTaskList(result.data)); dispatch(loadFinshAction); },1000) }) }) ``` ### 4.4.5 react-redux 将provider中的state和disPatch选择性地引入到组件中,使用react-redux中的connect方法 **注意:** react-redux只是组件与redux的连接工具,并不包含redux 参考: https://www.jianshu.com/p/8b0ec99a6714 https://www.cnblogs.com/vvjiang/p/9505646.html ## 4.5 组件内容开发 代码路径:src\Page\uncompleteTask.js ```javascript class UncompleteTask extends React.Component { getList(){ if(!this.props.isLoad){ this.props.getTaskList(); } } finshTask(id){ const tasks = [...this.props.tasks]; tasks.map(function(taskItem){ if(taskItem.id == id){ taskItem.status = "1"//完成 } }); this.props.updateTask(tasks); } addTask(){ const tasks = [...this.props.tasks]; var value = this.refs.taksInput.value; const task ={}; task.text =value; task.status = "0"; task.id = this.UUid(); tasks.push(task); this.props.updateTask(tasks); } deleteTask(id){ const tasks = [...this.props.tasks]; tasks.map(function(taskItem,index){ if(taskItem.id == id){ tasks.splice(index, 1) } }); this.props.updateTask(tasks); } UUid(){ var s = []; var hexDigits = "0123456789abcdef"; for (var i = 0; i < 32; i++) { s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); } s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010 s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01 s[8] = s[13] = s[18] = s[23]; var uuid = s.join(""); return uuid; } createTaskList(tasks){ var _this = this; var tasks = tasks.filter(item=>item.status =="0"); return tasks.map(function(task){ return
{task.text}
}) } componentDidMount(){ this.getList(); } render() { console.log("render",this.props); return (


未完成任务内容 {this.props.isLoad==false?"加载中":""} {this.createTaskList(this.props.tasks)}
) } } ``` ## 4.5 结果预览 ![输入图片说明](https://images.gitee.com/uploads/images/2020/0131/204830_af96292c_717356.png "15804748347923.png")