# 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、原型设计:
未完成任务:

已完成任务:

# 3、数据接口设计:
## 3.1、获取任务列表
接口地址如下:http://yapi.demo.qunar.com/mock/63161/getTaskList?type=0

# 4、系统开发
## 4.1 系统初始化
需要先安装node环境,安装create-react-app全局方法
E:\MyProjects> npx create-react-app my-appto-do-practice

## 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 (
);
}
}
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://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

配置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 结果预览
