实现todos案例

demo

我们要实现这样的一个对任务增删改查的案例,我们需要在这个文本框输入任务名称敲击回车的时候把这个任务添加到下面的任务列表中、点击任务前面的复选框吧任务变成已完成或者未完成状态、点击任务后面的删除按钮删除任务、底部显示未完成任务的数量根据状态筛选任务,清除全部已完成任务。

初始化项目

新建todos项目,创建components文件夹,删除多余文件把App.js移动到components文件夹中,我们把拆分为三个组件,顶部是一个组件,主体一个组件,底部是一个组件

create-react-app todos
cd todos
mkdir src/components
mv src/APP.js src/componetns/App.js
touch src/comoneents/AddTodo.js
touch src/comoneents/TodoList.js
touch src/comoneents/TodoExtra.js
npm start

注意: 运行项目之前需要开启项目对装饰器语法的支持

头部组件

import React, { Component } from 'react'

class AddTodo extends Component {
    render() {
        return <header className="header">
            <h1>todos</h1>
            <input className="new-todo" placeholder="What needs to be done?" />
        </header>
    }
}

export default AddTodo

主体组件

import React, { Component } from 'react'

class todoList 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>Taste JavaScript</label>
                        <button className="destroy"></button>
                    </div>
                    <input className="edit" />
                </li>
                <li>
                    <div className="view">
                        <input className="toggle" type="checkbox" />
                        <label>Buy a unicorn</label>
                        <button className="destroy"></button>
                    </div>
                    <input className="edit" />
                </li>
            </ul>
        </section>
    }
}

export default todoList

底部组件

import React, { Component } from 'react'

class TodoExtra extends Component {
    render() {
        return <footer className="footer">
            <span className="todo-count"><strong>0</strong> item left</span>
            <ul className="filters">
                <li>
                    <button className="selected">All</button>
                </li>
                <li>
                    <button>Active</button>
                </li>
                <li>
                    <button>Completed</button>
                </li>
            </ul>

            <button className="clear-completed">Clear completed</button>
        </footer>
    }
}

export default TodoExtra

构建mobx工作流

下载mobxmobx-react

npm install mobx mobx-react

新建stores文件夹,创建todo案例store

class TodoStore {
    todos = []
}

const todo = new TodoStore()


export default todo

使用provider组件把store 放到全局可以访问的地方

import React from 'react'
import ReactDOM from 'react-dom'
import App from './component/App'
import './index.css'
import { Provider} from 'mobx-react'
import TodoStore from './stores/Todo.store'

ReactDOM.render(
  <Provider todo={TodoStore}>
    <App />
  </Provider>,
  document.getElementById('root')
);

在三个组件中使用injectobserver装饰器分别来注入与订阅更新

import { inject, observer } from 'mobx-react'
import React, { Component } from 'react'

@inject('todo')
@observer
class ......

任务添加功能

用户可以输入任务名称,当按下回车的时候添加任务,我们可以给文本框添加键盘按下事件,触发事件后判断文本框是否有值,如果有值则添加到任务列表的数组中。

先定义添加到任务列表数组的action方法

 @action.bound todoAdd = function(taskName) {
   this.todos.push({
     taskName
   })
 }

监听输入框键盘事件,判断输入框输入的置是否有效,如果有效则调用添加任务方法

class AddTodo extends Component {
    addTodo(event) {
        const { todo } = this.props
        // 判断是否是键盘按下
        if(event.key === 'Enter') {
            // 获取任务名称
            const taskName = event.target.value
            // 判断是否输入了任务名称
            if( taskName.trim().length ===0 ) {
                return
            }   
            // 调用添加到任务列表数组
            todo.todoAdd(taskName)
            // 清空文本框内容
            event.target.value = ''
        }
    }   

    render() {
      
        return <header className="header">
            <h1>todos</h1>
            <input onKeyUp={this.addTodo.bind(this)} className="new-todo" placeholder="What needs to be done?" />
        </header>
    }
}

任务列表展示

循环绑定

class todoList extends Component {
    render() {
        const { todos } = this.props.todo

        return <section className="main">
            <input className="toggle-all" type="checkbox" />
            <ul className="todo-list">
                {
                    todos.map(todo => (
                        // className="completed"
                        <li  >
                            <div className="view">
                                <input className="toggle" type="checkbox" />
                                <label>{todo.taskName}</label>
                                <button className="destroy"></button>
                            </div>
                            <input className="edit" />
                        </li>
                    ))
                }
            </ul>
        </section>
    }
}

实现任务删除功能

store中添加删除任务方法根据下标删除

 @action.bound todoDelete (index) {
   this.todos.splice(index,1)
 }

在视图中绑定删除按钮方法并传入下标

todos.map((todo, index) => (
  // className="completed"
  <li  >
    <div className="view">
      <input className="toggle" type="checkbox" />
      <label>{todo.taskName}</label>
      {/* 删除按钮 */}
      <button className="destroy" onClick={() => todoDelete(index)}></button>
    </div>
    <input className="edit" />
  </li>
))å

更改任务完成状态

点击任务前面的复选框切换任务的完成与未完成状态,这个需要增加一个状态属性这个属性的值是一个布尔值,这个值默认为未完成。我们只需要把复选框和这个值绑定就可以了,再点击的时候切换任务状态。

store中添加更改完成/未完成状态方法,根据索引来更改

 @action.bound chengeCompleted (index, flag) {
   this.todos[index].isCompleted = flag
 }

任务列表组件中,根据是否选中来添加是否选中样式,给复选框添加选中状态,绑定改变事件并调用更改状态方法

 {
   todos.map((todo, index) => (
     // 判断是否为选中样式
     <li className={ todo.isCompleted ? 'completed' : '' }>
       <div className="view">
         {/* 设置选中状态,绑定改版事件调用改变store中状态方法 */}
         <input className="toggle" type="checkbox" onChange={(event) => { chengeCompleted(index, event.target.checked) }} checked={todo.isCompleted} />
         <label>{todo.taskName}</label>

         <button className="destroy" onClick={() => todoDelete(index)}></button>
       </div>
       <input className="edit" />
     </li>
   ))
 }

计算未完成任务的数量

因为总数根据未完成任务条数不停改变,所以我们需要使用计算值,使用在计算值方法中查找未完的条数

@computed get unfinishedTodoCount () {
  return this.todos.filter(todo => todo.isCompleted === false).length
}

视图中直接使用

class TodoExtra extends Component {
    render() {
        const { unfinishedTodoCount } = this.props.todo
        return <footer className="footer">
            <span className="todo-count"><strong>{ unfinishedTodoCount }</strong> item left</span>
            <ul className="filters">
                <li>
                    <button className="selected">All</button>
                </li>
                <li>
                    <button>Active</button>
                </li>
                <li>
                    <button>Completed</button>
                </li>
            </ul>

            <button className="clear-completed">Clear completed</button>
        </footer>
    }
}

实现任务筛选功能

对任务列表进行筛选使用一个值来控制当前选中状态,还需要一个计算值来返回当前选中状态的数据

@action.bound chengeFilter(condtition) {
  this.filter = condtition
}

@computed get filterTodo() {
  switch (this.filter) {
    case 'All':
      return this.todos;
    case 'Active':
      return this.todos.filter(todo => todo.isCompleted === false);
    case 'Completed':
      return this.todos.filter(todo => todo.isCompleted === true);
  }
}

在视图中去绑定点击事件调用切换状态方法

class TodoExtra extends Component {
    render() {
        const { unfinishedTodoCount, chengeFilter, filter } = this.props.todo
        return <footer className="footer">
            <span className="todo-count"><strong>{ unfinishedTodoCount }</strong> item left</span>
            <ul className="filters">
                <li>
                    <button className={filter === 'All' ? 'selected' : ''} onClick={ () => { chengeFilter('All') }}>All</button>
                </li>
                <li>
                    <button className={filter === 'Active' ? 'selected' : ''} onClick={ () => { chengeFilter('Active') }}>Active</button>
                </li>
                <li>
                    <button className={filter === 'Completed' ? 'selected' : ''} onClick={ () => { chengeFilter('Completed') }}>Completed</button>
                </li>
            </ul>

            <button className="clear-completed">Clear completed</button>
        </footer>
    }
}

在任务列表组件中去循环返回当前选中状态的数据的计算值

const { filterTodo, todoDelete, chengeCompleted } = this.props.todo

filterTodo.map((todo, index) => (......)

实现清除全部方法

直接让任务列表等于一个空数组

@action.bound todoAllClear(condtition) {
  this.todos = []
}

视图中绑定清除全部方法

const { unfinishedTodoCount, chengeFilter, filter, todoAllClear } = this.props.todo
......
<button className="clear-completed" onClick={todoAllClear}>Clear completed</button>