美文网首页
14. 完善第二个react项目(bulma,prop-type

14. 完善第二个react项目(bulma,prop-type

作者: dwy_interesting | 来源:发表于2018-11-07 11:18 被阅读0次

    在原有项目基础上实现:

    1. enter键提交输入框内容
    2. 点击添加键后聚焦输入框
    3. 实现删除任务
    4. 实现任务状态切换
    5. 实现任务数目统计
    6. bulma界面美化
    7. classnames判断条件添加className
    8. prop-types数据检测

    在项目目录下安装bulma,prop-types,classnames
    命令:cnpm install bulma prop-types classnames --save
    在项目public目录下的index.html文件中引入bulma的css

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
    

    代码:
    在src目录下:
    index.js

    import React from 'react';
    import ReactDom from 'react-dom';
    import TodoApp from './TodoApp';
    //渲染TodoApp内的内容到id位root的选择器
    ReactDom.render(
        <TodoApp />,
        document.querySelector("#root")
    )
    

    TodoApp.js

    import TodoHeader from './component/TodoHeader';//引入TodoHeader组件
    import TodoInput from './component/TodoInput';//引入TodoInput组件
    import TodoList from './component/TodoList';//引入TodoList组件
    import TodoFooter from './component/TodoFooter';//引入TodoFooter组件
    import React, { Component } from 'react'
    //TodoApp组件
    export default class TodoApp extends Component {
      //定义state存储list中要显示的数据
      constructor(){
        super();
        this.state = {
          todos: [{
            id: Math.random(),
            text: '哈哈',
            iscompleted: true
          }, {
            id: Math.random(),
            text: '大大',
            iscompleted: false
          }, {
            id: Math.random(),
            text: '小小',
            iscompleted: true
          }]
        }
      }
      //addList放啊对Input输入数据进行处理
      addToList = (text)=>{
        this.setState({//利用setState改变state的值
          todos:[//对所有的todos数据操作
              ...this.state.todos,
              {
                text,//text:text,将Input内输入的值写入state的todos
                id: Math.random(),//id取随机数
                iscompleted: true//将完成状态默认设置为true
              }
          ]
        })
      }
      //根据id对List数据进行删除的方法
      ondeleteItem=(id)=>{
        //过滤掉传入的id值所指定的数据
        /*const todos = this.state.todos.filter(todo=>{//找到所有的与传入id不同的数据
          return todo.id !=id;
        });*/
        //简写过滤
        const todos = this.state.todos.filter(todo=>todo.id!==id);
        this.setState({
          todos//todos:todos将剩下的数据再赋值给state的todos
        })
      }
      //根据id对list数据进行完成状态修改
      onchangeStatus=(id)=>{
        const todos = this.state.todos.map(todo=>{
          //找到需要修改状态的数据
          if(todo.id===id){
            //将该条数据中的iscomplited的布尔值取反
            todo.iscompleted = ! todo.iscompleted;
          }
          return todo;
        });
        this.setState({
          todos
        });
      }
      //渲染显示TodoHeader、TodoInput、TodoList等组件
      render() {
        //定义变量保存总任务数量(state中todos的长度)
        const totalCount = this.state.todos.length;
        //定义变量保存未完成的任务数(过滤iscomplited!==true后返回的数组的长度)
        const uncompletedCount = this.state.todos.filter(todo=>todo.iscompleted!==true).length;
        //定义变量保存已完成的任务数(过滤iscomplited===true后返回的数组的长度)
        const completedCount = this.state.todos.filter(todo=>todo.iscompleted===true).length;
        return (
          <div>
            <TodoHeader />
            <section className="hero is-light">
              <div className="hero-body">
                <div className="container">
                  {/* 将addToList数据处理函数传出去 */}
                  <TodoInput addToList={this.addToList}/>
                  {/* 通过state内的数据控制List的显示 */}
                  <TodoList todos={this.state.todos} ondeleteItem={this.ondeleteItem} onchangeStatus={this.onchangeStatus}/>
                  {/* 渲染显示Footer中的数值 */}
                  <TodoFooter totalCount={totalCount} uncompletedCount={uncompletedCount} completedCount={completedCount} />
               </div>
              </div>
            </section>
          </div>
        )
      }
    }
    

    src目录的component目录下:
    TodoHeader.js

    /*
    //第一种TodoHeader组件定义方法
    import React from 'react'
    //TodoHeader组件
    const TodoHeader = ()=>{
        return (
            <div>
                <h2>点击按钮添加输入框内容到列表</h2>
            </div>
        )
    }
    //导出TodoHeader
    export default TodoHeader;*/
    //第二种TodoHeader组件定义方法
    import React, { Component } from 'react'
    export default class TodoHeader extends Component {
      render() {
        return (
          <section className="hero is-primary">
          <div className="hero-body">
            <div className="container">
              <h1 className="title">
                待办事项列表
              </h1>
              <h2 className="subtitle">
                将输入框中的输入显示到表格并进行操作
              </h2>
            </div>
          </div>
        </section>
        )
      }
    }
    
    
    

    TodoInput.js

    import React, { Component , createRef} from 'react'
    //TodoInput组件
    export default class TodoInput extends Component {
      //添加数据管理state保存input内的值
      constructor(){
        super();
        this.state={inputValue:''};
        this.inputRef= createRef();
      } 
      //每次输入完成调用
      changeHandler=(e)=>{
        this.setState({//将当前input输入的数据存入state的inputValue
            inputValue: e.currentTarget.value//e.currentTarget当前改变事件指向的input输入框
        })
      }
      //每次输入之后点击添加按钮调用
      clickHandler=()=>{
        //当input输入框为空时,无法实现添加
        if(this.state.inputValue ===''){
          return;
        }
        //调用addToList数据处理函数对当前inputValue里的值进行处理
        this.props.addToList(this.state.inputValue);
        //通过setState设置state的inputValue的值
        this.setState({
          //将inputValue的值设置为空字符串
          inputValue:''
        });
        //点击添加后保持input输入框聚焦
        this.inputRef.current.focus();
      }
      //触发enter键处理提交数据
      keyupHandler=(e)=>{
        //当触发enter键
        if(e.keyCode===13){
          //调用添加按钮的处理函数实现与点击按钮相同后的相同效果
          this.clickHandler()
        }
      }
      render() {
        return (
          // <div>
          //   {/*onChange={this.changeHandler}当输入框数据有变动时调用函数changeHandler将输入的数据放入inputValue  */}
          //   {/*value={this.state.inputValue}在将输入的值放入inputValue之后,再次从state里面取出inputValue的值再放入输入框  */}
          //   <input type="text" ref={this.inputRef} onChange={this.changeHandler} value={this.state.inputValue} onKeyUp={this.keyupHandler}/>
          //   {/*onClick = {this.clickHandler}当处理完输入框的数据后,调用clickHandler函数对input的值进行加入List的处理  */}
          //   <button onClick = {this.clickHandler}>添加</button>
          // </div>
           <div className="field has-addons">
           <div className="control">
           <input type="text" className="input" ref={this.inputRef} onChange={this.changeHandler} value={this.state.inputValue} onKeyUp={this.keyupHandler}/>
           </div>
           <div className="control">
             <button className="button is-primary" onClick = {this.clickHandler}>添加</button>
           </div>
         </div>
        )
      }
    }
    
    
    

    TodoList.js

    import React, { Component } from 'react';
    import classNames from 'classnames';//引入classnames
    import PropTypes from 'prop-types';//引入prop-types
    //TodoList组件
    export default class TodoList extends Component {
    /*//方法三:在constructor中改变this指向
      constructor(){
          super();
          //改变this指向,避免性能问题
          this.deleteHandler = this.deleteHandler.bind(this);
      }*/ 
      //props数据检测
      static propTypes = {
        todos: PropTypes.arrayOf(//数组检测
          PropTypes.shape({//对象检测
            id: PropTypes.number.isRequired,//数字检测
            text: PropTypes.string.isRequired,//字符串检测
            iscompleted: PropTypes.bool.isRequired//布尔值检测
          }).isRequired
        ).isRequired
      }
      deleteHandler(id){
        //调用ondeleteItem传递需要删除的id
        this.props.ondeleteItem(id);
      }
      changeStatus(id){
        //调用onchangeStatus传递需要更改状态的id
        this.props.onchangeStatus(id);
      }
      render() {
        return (
            <table className="table is-striped">
            <thead>
              <tr>
              <th>id</th>
              <th>任务</th>
              <th>完成状态</th>
              <th>操作</th>
              </tr>
            </thead>
            <tbody>
              {//利用map实现循环,对todos里的数据进行遍历,每一条数据均为一个todo
                this.props.todos.map(todo => {//每条数据返回显示一条带有自身(todo)的数据的行
                  //将判断条件todo.iscompleted===true用一个变量iscompleted保存下来
                  const iscompleted = todo.iscompleted===true;
                  return  (
                    <tr key={todo.id}>
                      <td>{todo.id}</td>
                      <td>{todo.text}</td>
                      <td>
                      {/* //当isCompleted为true时显示已完成,否则显示未完成 */}
                      {/* {todo.iscompleted === true ?<span>已完成</span>:<span>未完成</span>} */}
                      {/* 优化 利用classnames的classNames方法对span元素的className进行判断处理,当满足iscompleted(即todo.iscompleted===true)时,
                      添加className为is-primary且显示已完成,不满足时,添加className为is-warning且显示未完成*/}
                      <span className={classNames("tag",{
                        "is-primary" : iscompleted,
                        "is-warning" : !iscompleted
                      })}>{iscompleted? "已完成":"未完成"}</span>
                      </td>
                      <td>
                        <div className="buttons has-addons">
                            {/*方法一:this.deleteHandler.bind(this,todo.id)利用bind不执行函数情况下改变this指向,并将id传递过来  */}
                          <button className="button is-warning" onClick={this.deleteHandler.bind(this, todo.id)}>删除</button>
                            {/*方法二:利用箭头函数解决this指向问题,直接调用deleteHandler函数并将id作为参数传递  */}
                            {/* <button ondelete={()=>{this.deleteHandler(todo.id)}}>删除</button> */}
                          <button className="button is-primary" onClick={this.changeStatus.bind(this, todo.id)}>切换状态</button>
                        </div>
                      </td>
                    </tr>
                  )
                })
              }
            </tbody>
            </table> 
        )
      }
    }
    
    

    TodoFooter.js

    import React, { Component } from 'react';
    import PropTypes from 'prop-types';//引入prop-types
    export default class TodoFooter extends Component {
      //定义静态的propTypes对props中的数据进行检测totalCount: PropTypes.number.isRequired(totalCount是必须有输入/存在的数据类型)
      static propTypes = {
        totalCount: PropTypes.number.isRequired,
        uncompletedCount: PropTypes.number,
        completedCount: PropTypes.number
      }
      render() {
        //从props中解构出totalCount,completedCount,uncompletedCount
        const{totalCount,completedCount,uncompletedCount} = this.props
        return (
          <div>
            <div>总任务:{totalCount}</div>
            <div>已完成任务:{uncompletedCount}</div>
            <div>未完成任务:{completedCount}</div>
          </div>
        )
      }
    }
    
    

    相关文章

      网友评论

          本文标题:14. 完善第二个react项目(bulma,prop-type

          本文链接:https://www.haomeiwen.com/subject/pjoyxqtx.html