11-07-react的todomvc代码解析

在这里插入图片描述

components文件夹

AddTodo.jsx

import React, {Component} from 'react'
import store from "../store/index"
import { addOneTodoAction } from "../store/actions/todos"

export default class AddTodo extends Component{
    constructor(props) {
        super(props);
        // 创建一个ref容器
        this.myInputRef = React.createRef();

        this.state = store.getState(); // 获取redux中的todos状态
    }
    componentDidMount() {
        store.subscribe(()=>{
            this.setState(store.getState())
        })
    }

    _handleKeyDown(e){
        let { todos } = this.state;
        let lastTodoId = todos.length === 0 ? 0 : todos[todos.length-1].id

        if(e.keyCode === 13){
            // 获取输入框中的内容
           let value = this.myInputRef.current.value;
           if(!value.trim()){
               alert("输入的内容不能为空~")
               return;
           }

           let todo = { id:lastTodoId+1, content:value, done:false }

           let action = addOneTodoAction(todo);
           store.dispatch(action);

           this.myInputRef.current.value = ""; // 清空输入框中的内容
        }
    }
    render() {
        return (
            <div className="todo-header">
                <input
                    type="text"
                    ref={this.myInputRef}
                    placeholder="请输入今天的任务清单,按回车键确认"
                    onKeyDown={ e=>this._handleKeyDown(e) }
                />
            </div>
        )
    }
}

Footer.jsx

import React, {Component} from 'react'

import store from "../store/index"
import { delDoneTodo,isCheckedAllTodo } from "../store/actions/todos"

export default class Footer extends Component {
    constructor(props){
        super(props);
        this.state = store.getState();
    }
    componentDidMount() {
        store.subscribe(()=>{
            this.setState(store.getState())
        })
    }

    render() {
        let { todos,finishedCount } = this.state;
        return (
            <div className="todo-footer">
                <label>
                    <input type="checkbox"
                           checked={ todos.length>0 && finishedCount === todos.length  }
                           onChange={ (e)=>this._dealChecked(e) }
                    />
                </label>
                <span><span>已完成{ finishedCount }</span> / 总计{todos.length}</span>
                <button
                    className="btn btn-warning"
                    onClick={()=>this._dealRemove()}
                >清除已完成任务</button>
            </div>
        )
    }
    _dealRemove(){
        let action = delDoneTodo();
        store.dispatch(action)
    }
    _dealChecked(e){
        let action = isCheckedAllTodo(e.target.checked);
        store.dispatch(action)
    }
}

Todo.jsx

import React, {Component} from 'react'
import PropTypes from "prop-types"

import store from "../store/index"
import { delOneTodoAction, changeOneTodoAction } from "../store/actions/todos"

export default class Todo extends Component{
    static propTypes = {
        todo:PropTypes.object.isRequired
    }
    constructor(props){
        super(props);
        // 状态机
        this.state = {
            isShowDelBtn :false  // 控制删除按钮是否显示
        }

    }
    _hasShowBtn(e, flag){
        // 更新状态机
        this.setState({
            isShowDelBtn:flag
        })
    }
    render() {
        let { todo } = this.props;
        let { isShowDelBtn } = this.state;
        return (
            <li
                onMouseOver={ e=> this._hasShowBtn(e,true) }
                onMouseOut={ e=> this._hasShowBtn(e,false) }
            >
                <label>
                    <input
                        type="checkbox"
                        checked={ todo.done }
                        onClick={ ()=>this._dealChange(todo.id, !todo.done) }
                        readOnly
                    />
                    <span>{todo.content}</span>
                </label>
                <button
                    className="btn btn-warning"
                    style={{ display: isShowDelBtn ? "block" : "none" }}
                    onClick={ ()=>this._delOneTodo(todo.id) }
                >
                    删除
                </button>
            </li>
        )
    }
    _dealChange(todoId,flag){
        // console.log(todoId,flag)
        let action = changeOneTodoAction(todoId,flag);
        store.dispatch(action)
    }
    _delOneTodo(todoId){
        // console.log(todoId)
        let action = delOneTodoAction(todoId)
        store.dispatch(action);
    }

}

TodoList.jsx

import React, {Component} from 'react'
import Todo from "./Todo"
import store from "../store/index"

export default class TodoList extends Component {
    constructor(props){
        super(props);
        // 状态机
        this.state = store.getState();
    }
    componentDidMount() {
        // 订阅
        store.subscribe(()=>{
            this.setState(store.getState()); // 更新状态机
        })
    }

    render() {
        let { todos } = this.state;
        console.log(todos)
        return (
            <ul className="todo-main">
                {
                    todos.map((todo,index)=>(
                        <Todo
                            key={todo.id}
                            todo={todo}
                        ></Todo>
                    ))
                }
            </ul>
        )
    }
}

http

index.js

import axios from 'axios'

// 请求超时时间
axios.defaults.timeout = 10000;
// post的请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8';

// 配置请求拦截器
axios.interceptors.request.use((config)=>{
    return config;
}, (error)=>{
    return Promise.error(error);
});

// 配置响应拦截器
axios.interceptors.response.use((response)=>{
    // 过滤
    if(response.status === 200){
        return Promise.resolve(response.data);
    }else {
        return Promise.reject(response.data);
    }

}, (error)=>{
    console.log(error);
});

export  default function ajax(url = '', params = {}, type = 'GET') {
    // 0. 变量
     let promise;

    // 1. 返回promise
    return new Promise((resolve, reject)=>{
         // 1.1 判断请求的类型
        if(type.toUpperCase() === 'GET'){ // get请求
            promise = axios({
                url,
                params
            })
        }else if(type.toUpperCase() === 'POST'){ // post请求
            promise = axios({
                method: 'post',
                url,
                data: params
            })
        }

        //  1.2 处理结果并返回
        promise.then((response)=>{
            resolve(response);
        }).catch((error)=>{
            reject(error);
        })
    });
}

store

actions

todos.js

import {
        GET_ALL_TODO,
        DEL_ONE_TODO,
        CHANGE_ONE_TODO,
        ADD_ONE_TODO,
        DEL_DONE_TOOD,
        IS_CHECKED_ALL_TODO
}
from "../contains/actionTypes"

/*export function getAllTodoAction(todos) {
    return{
        type:GET_ALL_TODO,
        todos
    }
}*/

// 1)存储所有的todo
export const getAllTodoAction = todos=> ({
        type:GET_ALL_TODO,
        todos
})

// 2)删除一个todo
export const delOneTodoAction = todoId=> ({
        type:DEL_ONE_TODO,
        todoId
})

// 3)修改一个todo的状态
export const changeOneTodoAction = (todoId,isDone) => ({
        type:CHANGE_ONE_TODO,
        todoId,
        isDone
})

// 4)添加一个todo
export const addOneTodoAction = (todo) => ({
        type:ADD_ONE_TODO,
        todo
})

// 5)删除已经完成的todo
export const delDoneTodo = () => ({
        type:DEL_DONE_TOOD,
})

// 6)实现全选和反选
export const isCheckedAllTodo = (flag) => ({
        type:IS_CHECKED_ALL_TODO,
        flag
})

// store.dispatch(incremtn(10))
// store.dispatch(getAllTodoAction([{},{},{}]))

contains

actionType.js

// 1)存储所有todos  (调用接口获取所有的todos,存储到redux中,让所有的组件可以共享todos状态)
export const GET_ALL_TODO = "GET_ALL_TODO"

// 2)删除一个todo
export const DEL_ONE_TODO = "DEL_ONE_TODO"

// 3)修改一个todo的状态
export const CHANGE_ONE_TODO = "CHANGE_ONE_TODO"

// 4)添加一条todo
export const ADD_ONE_TODO = "ADD_ONE_TODO"

// 5)删除已经完成的todo
export const DEL_DONE_TOOD = "DEL_DONE_TOOD"

// 6)实现全选和反选
export const IS_CHECKED_ALL_TODO = "IS_CHECKED_ALL_TODO"

reducers

index.js

import {
    GET_ALL_TODO,
    DEL_ONE_TODO,
    CHANGE_ONE_TODO,
    ADD_ONE_TODO,
    DEL_DONE_TOOD,
    IS_CHECKED_ALL_TODO
} from "../contains/actionTypes"

// 初始化状态
const defaultState = {
    todos:[],
    finishedCount : 0
}
export default function reducer(state=defaultState,action){
    // 1)存储所有的todo
    if(action.type === GET_ALL_TODO){
        // reducer 根据一个老状态 + action 返回一个新状态
        const newState = JSON.parse(JSON.stringify(state)); // 深copy
        newState.todos = action.todos;
        return newState;
    }

    // 2)删除一条todo
    if(action.type === DEL_ONE_TODO){
        const newState = JSON.parse(JSON.stringify(state));
        let tempFinishedCount = 0;
        newState.todos.forEach((todo,index)=>{
            if(action.todoId === todo.id){
                newState.todos.splice(index,1)
            }
        })
        newState.todos.forEach((todo,index)=>{
            if(todo.done){
                tempFinishedCount += 1;
            }
        })
        // 我们要newState上面添加一个属性叫finishedCount
        newState.finishedCount = tempFinishedCount
        return newState;
    }

    // 3)修改todo的状态
    if(action.type === CHANGE_ONE_TODO){
        const newState = JSON.parse(JSON.stringify(state));
        let tempFinishedCount = 0;
        newState.todos.forEach((todo,index)=>{
            if(action.todoId === todo.id){
               todo.done = action.isDone
            }
        })
        newState.todos.forEach((todo,index)=>{
            if(todo.done){
                tempFinishedCount += 1;
            }
        })
        // 我们要newState上面添加一个属性叫finishedCount
        newState.finishedCount = tempFinishedCount
        return newState;
    }

    // 4)添加一个todo
    if(action.type === ADD_ONE_TODO){
        const newState = JSON.parse(JSON.stringify(state));
        newState.todos.push(action.todo)
        return newState;
    }

    // 5)删除已经完成的todo
    if(action.type === DEL_DONE_TOOD){
        const newState = JSON.parse(JSON.stringify(state));
        let tempArr = [];
        newState.todos.forEach((todo,index)=>{
            if(!todo.done){
                // 没有完成
                tempArr.push(todo)
            }
        })
        // 我们要newState上面添加一个属性叫finishedCount
        newState.finishedCount = 0
        newState.todos = tempArr;
        return newState;
    }

    // 6)实现全选和反选
    if(action.type === IS_CHECKED_ALL_TODO){
        const newState = JSON.parse(JSON.stringify(state));
        let tempFinishedCount = 0;
        newState.todos.forEach((todo,index)=>{
            todo.done = action.flag
        })
        newState.todos.forEach((todo,index)=>{
            if(todo.done){
                tempFinishedCount += 1;
            }
        })
        // 我们要newState上面添加一个属性叫finishedCount
        newState.finishedCount = tempFinishedCount
        return newState;
    }
}

index.js

import { createStore } from "redux"
import reducer from "./reducers/index"

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
let store = createStore(reducer,composeEnhancers());

export default store; // 导出仓库,在组件中就可以导入仓库


App.js

import React from 'react';
import "./index.css"

import AddTodo from './components/AddTodo'
import TodoList from './components/TodoList'
import Footer from './components/Footer'

import store from "./store/index"
import { getAllTodoAction } from "./store/actions/todos"

class App extends React.Component {
    componentWillMount() {
        // 模拟ajax请求获取todos
        const todos = [
            {id:1,content:"学习uniapp",done:false},
            {id:2,content:"学习vue",done:true},
            {id:3,content:"学习webpack",done:false},
            {id:4,content:"学习react",done:true},
            {id:5,content:"学习redux",done:false}
        ]
        // 把数据存储到redux中,目的:共享
        let action = getAllTodoAction(todos);
        store.dispatch(action)
    }

    render(){
        return (
            <div className="todo-container">
                <div className="todo-wrap">
                    <AddTodo />
                    <TodoList />
                    <Footer/>
                </div>
            </div>
        )
    }
}

export default App;

index.css

body {background: #fff;}

.btn {
  display: inline-block;
  padding: 8px 10px;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
  border-radius: 4px;
}

.btn-warning {
  color: #fff;
  background-color: orange;
  border: none;
}

.btn-warning:hover {
  color: #fff;
  background-color: red;
}

.btn:focus {
  outline: none;
}


/*app*/
.todo-container {
  width: 600px;
  margin: 0 auto;
}
.todo-container .todo-wrap {
  padding: 10px;
  border: 1px solid #ddd;
  border-radius: 5px;
}

/*header*/
.todo-header input {
  width: 560px;
  height: 28px;
  font-size: 14px;
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 4px 7px;
  outline: none;
}

.todo-header input:focus {
  outline: none;
  border-color: rgba(255, 0, 0, 0.8);
  box-shadow: inset 0 1px 1px rgba(255, 0, 0, 0.075), 0 0 8px rgba(255, 0, 0, 0.6);
}

/*main*/
.todo-main {
  margin-left: 0;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding: 0;
}

.todo-empty {
  height: 40px;
  line-height: 40px;
  border: 1px solid #ddd;
  border-radius: 2px;
  padding-left: 5px;
  margin-top: 10px;
}
/*item*/
li {
  list-style: none;
  height: 36px;
  line-height: 36px;
  padding: 0 5px;
  border-bottom: 1px solid #ddd;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

li label {
  cursor: pointer;
}


li button.btn {
  display: none;
  margin-top: 3px;
  padding: 3px 10px;
}

li:before {
  content: initial;
}

li:last-child {
  border-bottom: none;
}

/*footer*/
.todo-footer {
  height: 40px;
  line-height: 40px;
  padding-left: 6px;
  margin-top: 5px;
}

.todo-footer label {
  display: inline-block;
  margin-right: 20px;
  cursor: pointer;
}

.todo-footer label input {
  position: relative;
  top: -1px;
  vertical-align: middle;
}

.todo-footer button {
  float: right;
  margin-top: 5px;
}

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App.js';

ReactDOM.render(<App />,  document.getElementById('root'));
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值