菜鸟程序员的react -- TodoList 之旅

前言

hello 大家好,我是一个菜鸟程序员,都说学习前端框架大多从 TodoList 学起,我呢,学习 react 也有一段时间了,特此做了一个 TodoList 做为自己的学习总结,顺便发个博客记录一下自己的学习,如有错漏欢迎大神在评论区指点。

环境安装

注意

第一行的 npx 不是拼写错误 —— 它是 npm 5.2+ 附带的 package 运行工具。

npx create-react-app my-app
cd my-app

删除掉新项目中 src/ 文件夹下的所有文件,并新建二个文件,index.js 和 index.css.

安装 antd,使用 UI 框架让我们的 TodoList 更加美观

npm install antd --save

分别复制以下代码到 index.js ,index.css

index.js

import React from "react";
import ReactDOM from "react-dom";
import { Button, Divider, Input, Row, Col } from "antd";
import "antd/dist/antd.css"; // or 'antd/dist/antd.less'
import "./index.css";
class List extends React.Component {
  render() {
    return (
      <div className="list">
        <Row className="list-item">
          <Col span={16}>col</Col>
          <Col span={4}><Button>修改</Button></Col>
          <Col span={4}><Button>删除</Button></Col>
        </Row>
      </div>
    );
  }
}
class Add extends React.Component {
  render() {
    return (
      <div className="add">
      <Input
        placeholder="请输入需要添加的事项"
        allowClear
      />
      <Button type="primary">添加</Button>
    </div>
    );
  }
}

class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  render() {
    return (
      <div className="todolist">
        <Divider>TodoList</Divider>
        <Add />
        <List />
      </div>
    );
  }
}

// ========================================

ReactDOM.render(<TodoList />, document.getElementById("root"));

index.css

*{
    margin: 0;
    padding: 0;
}
html,body{
    height: 100vh;
    overflow: hidden;
}
.todolist{
    width: 400px;
    height: 630px;
    margin: 100px auto;
    border: 2px solid #000;
    padding: 20px;
}
.list{
    height: 430px;
    overflow-y: scroll;
}
.list-item{
    margin-top: 20px;
}
.add{
    display: flex;
}
.performance{
    display: flex;
    justify-content: space-between;
}
.finish{
    text-decoration:line-through;
    text-line-through-color:red
}

启动项目,这时我们已经可以看到 TodoList 大致的样子了

npm start

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i51S1ZLM-1598491947715)(https://imgkr2.cn-bj.ufileos.com/d459b745-7625-49e0-b34d-bf091a2d0e77.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=ljjg8xj5BnxmjYFjZdJNU%252FSTX6s%253D&Expires=1598432071)]

编写添加功能

这个小案例,我采用的是所有数据都交由父组件处理,然后分发给子组件,所以我们先在父组件的 state 里面声明一个 text 和 list,text 用于存储输入框的值,list 用于存储添加的列表。

  constructor(props) {
    super(props);
    this.state = {
      list: [],
      text: "",
    };
  }

编写输入框的 change 事件,利用一起传入的text赋值给输入框的value属性实现数据的双向绑定并通过添加事件获取text来修改list,其中我们用时间戳来当做添加数据的id,完整代码如下

TodoList

class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [],
      text: "",
    };
  }
  // 绑定输入框的值
  handleChange(e) {
    e.persist();
    this.setState({
      text: e.target.value,
    });
  }
  //添加
  handleAdd() {
    if (!this.state.text) {
      alert("输入框不能为空");
      return;
    }
    const Itme = { text: this.state.text, id: Date.now()};
    this.setState({
      list: [Itme, ...this.state.list],
      text: "",
    });
  }
  render() {
    return (
      <div className="todolist">
        <Divider>TodoList</Divider>
        <Add
          change={(e) => this.handleChange(e)}
          text={this.state.text}
          addClick={() => this.handleAdd()}
        />
        <List list={this.state.list}/>
      </div>
    );
  }
}

Add

class Add extends React.Component {
  render() {
    return (
      <div className="add">
        <Input
          placeholder="请输入需要添加的事项"
          onChange={(e) => this.props.change(e)}
          allowClear
          value={this.props.text}
        />
        <Button type="primary" onClick={() => this.props.addClick()}>添加</Button>
      </div>
    );
  }
}

通过遍历父组件传过来的list生成当前列表

List

class List extends React.Component {
  render() {
    const list = this.props.list
    return (
      <div className="list">
      {list.map((item) => (
        <Row className="list-item" key={item.id}>
          <Col span={16} >{item.text}</Col>
          <Col span={4}>
            <Button type="primary">修改</Button>
          </Col>
          <Col span={4}>
            <Button type="primary" danger>删除</Button>
          </Col>
        </Row>
      ))}
      </div>
    );
  }
}

添加修改功能

在父组件的 state 里面声明一个 AddOrUpdate 变量来存储当前是处于添加状态还是修改状态及需要修改项的id,在默认的清空下是处于添加状态,可以通过修改按钮更改为修改状态,当修改完成后还原成添加状态

TodoList

class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [],
      text: "",
      AddOrUpdate: { isAdd: true },
    };
  }
  // 绑定输入框的值
  handleChange(e) {
    e.persist();
    this.setState({
      text: e.target.value,
    });
  }
  //添加 | 修改
  handleAdd() {
    if (!this.state.text) {
      alert("输入框不能为空");
      return;
    }
    if (this.state.AddOrUpdate.isAdd) {
      const Itme = { text: this.state.text, id: Date.now(), isFinish: false };
      this.setState({
        list: [Itme, ...this.state.list],
        text: "",
      });
    } else {
      let { id } = this.state.AddOrUpdate;
      let newlist = this.state.list;
      newlist.map((item) => item.id === id && (item.text = this.state.text));
      this.setState({
        list: [...newlist],
        AddOrUpdate: { isAdd: true },
        text: "",
      });
    }
  }
  //修改
  handleUpdate(item) {
    this.setState({
      AddOrUpdate: { isAdd: false, id: item.id },
      text: item.text,
    });
  }
  render() {
    return (
      <div className="todolist">
        <Divider>TodoList</Divider>
        <Add
          change={(e) => this.handleChange(e)}
          text={this.state.text}
          addClick={() => this.handleAdd()}
          AddOrUpdate={this.state.AddOrUpdate}
        />
        <List
          list={this.state.list}
          update={(item) => this.handleUpdate(item)}
        />
      </div>
    );
  }
}

Add

class Add extends React.Component {
  render() {
    return (
      <div className="add">
        <Input
          placeholder="请输入需要添加的事项"
          onChange={(e) => this.props.change(e)}
          allowClear
          value={this.props.text}
        />
        <Button type="primary" onClick={() => this.props.addClick()}>
          {this.props.AddOrUpdate.isAdd ? "添加" : "确定修改"}
        </Button>
      </div>
    );
  }
}

List

class List extends React.Component {
  render() {
    const list = this.props.list;
    return (
      <div className="list">
        {list.map((item) => (
          <Row className="list-item" key={item.id}>
            <Col span={16}>{item.text}</Col>
            <Col span={4}>
              <Button type="primary" onClick={() => this.props.update(item)}>
                修改
              </Button>
            </Col>
            <Col span={4}>
              <Button
                type="primary"
                danger
              >
                删除
              </Button>
            </Col>
          </Row>
        ))}
      </div>
    );
  }
}

添加删除功能

这里我是通过filter来筛除点击的列表项实现删除功能的

  //删除
  handleDelete(item) {
    this.setState({
      list: this.state.list.filter((i) => i.id !== item.id),
    });
  }

接下来我们来实现最后一个功能了,也就是查询功能

我的思路是这样子的,给每条数据增加一个自定义状态(完成和未完成),默认都是未完成,通过点击当前项来修改当前数据的状态,
设置三个按钮(全部,未完成,已完成),然后在父组件设置一个变量来存储这三个状态,通过分别点击这三个按钮来状态,然后把这个状态传递给列表组件(List组件),通过filter实现数据的筛选,因为我们所有的子组件的不包含state,所以我们可以优化一下都写成函数式组件

完整代码如下

import React from "react";
import ReactDOM from "react-dom";
import { Button, Divider, Input, Row, Col } from "antd";
import "antd/dist/antd.css"; // or 'antd/dist/antd.less'
import "./index.css";

function List(props) {
  let list = props.list;
  if(props.condition === 'finish'){
    list = list.filter(item=>item.isFinish)
  }
  if(props.condition === 'unfinished'){
    list = list.filter(item=>!item.isFinish)
  }
  return (
    <div className="list">
      {list.map((item) => (
        <Row className="list-item" key={item.id}>
          <Col span={16} onClick={() => props.isFinish(item)} className={item.isFinish ? 'finish': null}>
            {item.text}
          </Col>
          <Col span={4}>
            <Button type="primary" onClick={() => props.update(item)}>
              修改
            </Button>
          </Col>
          <Col span={4}>
            <Button
              onClick={() => props.deleteClick(item)}
              type="primary"
              danger
            >
              删除
            </Button>
          </Col>
        </Row>
      ))}
    </div>
  );
}
function Add(props) {
  return (
    <div className="add">
      <Input
        placeholder="请输入需要添加的事项"
        allowClear
        onChange={(e) => props.change(e)}
        value={props.text}
      />
      <Button type="primary" onClick={() => props.addClick()}>
        {props.AddOrUpdate.isAdd ? "添加" : "确定修改"}
      </Button>
    </div>
  );
}
function Performance(props) {
  return (
    <div className="performance">
      {
        props.status.map(item=>(
        <Button type="primary" onClick={()=>props.statusClick(item.state)} key={item.state}>{item.text}</Button>
        ))
      }
    </div>
  );
}
class TodoList extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [],
      text: "",
      AddOrUpdate: { isAdd: true },
      status:[
        {
          text:'全部',
          state:'All'
        },
        {
          text:'已完成',
          state:'finish'
        },
        {
          text:'未完成',
          state:'unfinished'
        }
      ],
      condition:'All'
    };
  }
  handleAdd() {
    if (!this.state.text) {
      alert("输入框不能为空");
      return;
    }
    if (this.state.AddOrUpdate.isAdd) {
      const Itme = { text: this.state.text, id: Date.now(),isFinish:false};
      this.setState({
        list: [Itme, ...this.state.list],
        text: "",
      });
    } else {
      let { id } = this.state.AddOrUpdate;
      let newlist = this.state.list;
      newlist.map((item) => item.id === id && (item.text = this.state.text));
      this.setState({
        list: [...newlist],
        AddOrUpdate: { isAdd: true },
        text: "",
      });
    }
  }
  handleDelete(item) {
    this.setState({
      list: this.state.list.filter((i) => i.id !== item.id),
    });
  }
  handleChange(e) {
    e.persist();
    this.setState({
      text: e.target.value,
    });
  }
  handleUpdate(item) {
    this.setState({
      AddOrUpdate: { isAdd: false, id: item.id },
      text: item.text,
    });
  }
  handleIsFinish(data) {
    let newlist = this.state.list;
    newlist.map((item) => item.id === data.id && (item.isFinish = !item.isFinish));
    this.setState({
      list: [...newlist],
    });
  }
  handleStatus(state){
    this.setState({
      condition:state
    })
  }
  render() {
    return (
      <div className="todolist">
        <Divider>TodoList</Divider>
        <Add
          addClick={() => this.handleAdd()}
          text={this.state.text}
          change={(e) => this.handleChange(e)}
          AddOrUpdate={this.state.AddOrUpdate}
        />
        <List
          list={this.state.list}
          deleteClick={(item) => this.handleDelete(item)}
          update={(item) => this.handleUpdate(item)}
          isFinish={(item) => this.handleIsFinish(item)}
          condition={this.state.condition}
        />
        <Performance status={this.state.status} statusClick={(state)=>this.handleStatus(state)}/>
      </div>
    );
  }
}

// ========================================

ReactDOM.render(<TodoList />, document.getElementById("root"));

总结

react --TodoList 学习之旅到此结束了,希望我的学习经验能让大家有所收获,如有更好的实现方法可以在评论区留言哦!!!

线上demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值