React实践项目--todolist(3)基本组件的编写与联调

todolist

真正的挑战要开始了~~~

这里我采用自顶向下的叙述方法,来描述组件的结构,大家可以先按照顺序依次阅读,最后再回头完整的看一遍,就能明白其中的设计方法啦~~

  1. App.jsx
    在根组件中,我们使用 TodoListContext.Consumer 将从模拟接口获取的数据,传递给数据消费者,即 TodoList 组件。

在未获取到数据前,使用 Loading 组件来渲染当前区域。

import "./App.css";
import Loading from "./components/Loading";
import TodoList from "./components/TodoList";
import { Http, TodoListContext } from "./utils";

function App() {
  return (
    <div className="App">
      <Http.Get url="xxx" loading={<Loading />}>
        <TodoListContext.Consumer>
          {(value) => <TodoList listData={value} />}
        </TodoListContext.Consumer>
      </Http.Get>
    </div>
  );
}

export default App;
  1. TodoList
import React from "react";
import "./index.css";
import TodoItem from "../TodoItem";
import InputItem from "../InputItem";
import TodoTabs from "../TodoTabs";

export default class TodoList extends React.Component {
  state = {
    filterType: "",
    listData: [],
    originData: [],
  };
  // 存储 listData
  async componentDidMount() {
    this.setState({
      listData: this.props.listData,
      originData: this.props.listData,
    });
  }

  // 点击 input: checkbox ,把当前任务置为已完成
  complate(name) {
    this.setState({
      listData: [
        ...this.state.listData.map((item) => {
          if (item.name === name) {
            item.isChecked = !item.isChecked;
          }
          return item;
        }),
      ],
    });
  }

  // 删除列表项
  deleteItem(name) {
    this.setState({
      listData: [...this.state.listData.filter((item) => item.name !== name)],
    });
  }
  // 增加列表项
  addItem(name) {
    if (name.trim()) {
      this.setState({
        listData: [
          ...this.state.listData,
          {
            name,
            isDelete: false,
            isChecked: false,
          },
        ],
      });
    }
  }

  // 设置当前过滤的类型
  filterListData(filter) {
    this.setState({
      filterType: filter,
    });
  }

  render() {
    const listData = this.state.listData.filter(
      (item) => !!item[this.state.filterType || "name"]
    );
    return (
      <div className="todo_wrapper">
        <InputItem
          listData={this.state.listData}
          addItem={this.addItem.bind(this)}
        />
        <TodoTabs
          filterType={this.state.filterType}
          filterListData={this.filterListData.bind(this)}
        />
        <ul>
          {listData.length ? (
            listData.map((item, index) => (
              <TodoItem
                key={index}
                data={item}
                complate={this.complate.bind(this, item.name)}
                deleteItem={this.deleteItem.bind(this, item.name)}
              />
            ))
          ) : (
            <div>没有数据</div>
          )}
        </ul>
      </div>
    );
  }
}
.todo_wrapper{
  width: 500px;
  margin: auto;
  padding: 50px 0;
  ul{
    padding: 0;
  }
}
  1. InputItem
    React.createRef 创建引用,来获取输入框的 value
import React from "react";
import "./index.scss";

export default class InputItem extends React.Component {
  constructor(props) {
    super(props);
    this.InputRef = React.createRef();
  }

  state = {
    addItem: "",
  };

  // 增加列表项
  addItem() {
    this.props.addItem(this.InputRef.current.value);
    this.InputRef.current.value = "";
  }

  render() {
    return (
      <div className="input_wrapper">
        <input type="text" placeholder="请输入待办事项" ref={this.InputRef} />
        <button onClick={() => this.addItem()}>增加</button>
      </div>
    );
  }
}

.input_wrapper {
  display: flex;
  align-items: center;
  height: 30px;

  input {
    flex: 1;
    height: 100%;
    margin-right: 10px;
    outline: none;
    padding-left: 1em;
    border: 1px solid #ccc;
  }

  button {
    width: 100px;
    height: 100%;
    border: none;
    cursor: pointer;
    transition: all .3s;

    &:hover {
      background-color: black;
      color: #fff;
    }
  }
}
  1. TodoTabs
    这个组件可以切换要展示的不同列表项:全部/已完成

当点击某个类型时,通过 props 调用父组件的 filterListData 方法过滤数据

import React from "react";
import "./index.scss";

export default class TodoTabs extends React.Component {
  state = {
    selIdx: 0,
    tabList: [
      {
        name: "全部",
        filter: "",
      },
      {
        name: "已完成",
        filter: "isChecked",
      },
    ],
  };

  // 选择某个数据类型
  setSelIdx(idx) {
    this.setState({
      selIdx: idx,
    });
    const { selIdx, tabList } = this.state;
    // 调用父组件的方法,并将类型通过参数传给父组件
    this.props.filterListData(tabList[selIdx].filter);
  }

  render() {
    return (
      <div className="tabs">
        {this.state.tabList.map((item, index) => (
          <div
            key={index}
            className={`tab ${this.props.filterType === item.filter ? "sel" : ""}`}
            onClick={()=>this.props.filterListData(item.filter)}
          >
            {item.name}
          </div>
        ))}
      </div>
    );
  }
}
.tabs {
  display: flex;

  .tab {
    width: 80px;
    margin: 20px 10px 10px 0;
    padding: 5px 10px;
    border: 1px solid #ccc;
    box-sizing: border-box;
    cursor: pointer;
    transition: all .3s;

    &.sel,
    &:hover {
      background-color: #000;
      color: #fff;
    }
  }
}
  1. TodoItem
    待办列表项
import React from "react";
import IsChecked from "../IsCheck";
import "./index.scss";

export default class TodoItem extends React.Component {
  render() {
    const { name, isChecked, isDelete } = this.props.data;
    return (
      <div className="todo_item">
        <div className="left">
          <IsChecked isChecked={isChecked} complate={this.props.complate} />
          <span className={isChecked ? "del" : ""}>{name}</span>
        </div>
        <button onClick={this.props.deleteItem}>删除</button>
      </div>
    );
  }
}
.todo_item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 30px;
  margin-bottom: 10px;

  .left {
    flex: 1;
    display: flex;
    align-items: center;
    margin-right: 20px;
    text-align: left;

    span.del {
      flex: 1;
      text-decoration: line-through;
    }
  }

  button {
    width: 100px;
    height: 100%;
    border: none;
    cursor: pointer;
    transition: all .3s;

    &:hover {
      background-color: black;
      color: #fff;
    }
  }
}
  1. IsChecked
    操作待办事项
import React from 'react';
import './index.scss'

export default class IsChecked extends React.Component{
  
  render(){
    return (
      <input type="checkbox" checked={this.props.isChecked} onChange={this.props.complate} />
    )
  }
}
input[type="checkbox"]{
  width: 20px;
  height: 20px;
  border-radius: 50%;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值