IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> React组件的组合使用-TodoList -> 正文阅读

[JavaScript知识库]React组件的组合使用-TodoList

一、功能界面的组件化编码流程(通用)

  1. 拆分组件: 拆分界面,抽取组件
  2. 实现静态组件: 使用组件实现静态页面效果
  3. 实现动态组件
    3.1 动态显示初始化数据
    3.1.1 数据类型
    3.1.2 数据名称
    3.1.2 保存在哪个组件?
    3.2 交互(从绑定事件监听开始)

二、组件化

  1. 显示所有todo列表
  2. 输入文本, 点击按钮显示到列表的首位, 并清除输入的文本
    3.效果:
    在这里插入图片描述

三、静态组件

2.1、创建组件

在这里插入图片描述

import React, { Component } from 'react'

export default class xxxx extends Component {
  render() {
    return (
      <div>x x x x</div>
    )
  }
}

2.3、引入静态文件

App.jsx

//导入核心库
import React, { Component } from "react";
import './App.css'

//创建外壳组件并暴露
export default class App extends Component {
  render() {
    // 把组件hello、welcome导入
    return (
      <div className="todo-wrap">
        <div className="todo-header">
          <input type="text" placeholder="请输入你的任务名称,按回车键确认" />
        </div>
        <ul className="todo-main">
          <li>
            <label>
              <input type="checkbox" />
              <span>xxxxx</span>
            </label>
            <button className="btn btn-danger" style={{ display: "none" }}>
              删除
            </button>
          </li>
          <li>
            <label>
              <input type="checkbox" />
              <span>yyyy</span>
            </label>
            <button className="btn btn-danger" style={{ display: "none" }}>
              删除
            </button>
          </li>
        </ul>
        <div className="todo-footer">
          <label>
            <input type="checkbox" />
          </label>
          <span>
            <span>已完成0</span> / 全部2
          </span>
          <button className="btn btn-danger">清除已完成任务</button>
        </div>
      </div>
    );
  }
}

App.css

/*base*/
body {
    background: #fff;
  }
  
  .btn {
    display: inline-block;
    padding: 4px 12px;
    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-danger {
    color: #fff;
    background-color: #da4f49;
    border: 1px solid #bd362f;
  }
  
  .btn-danger:hover {
    color: #fff;
    background-color: #bd362f;
  }
  
  .btn:focus {
    outline: none;
  }
  
  .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;
  }
  
  .todo-header input:focus {
    outline: none;
    border-color: rgba(82, 168, 236, 0.8);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  }
  
  /*main*/
  .todo-main {
    margin-left: 0px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding: 0px;
  }
  
  .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;
  }
  
  li label {
    float: left;
    cursor: pointer;
  }
  
  li label li input {
    vertical-align: middle;
    margin-right: 6px;
    position: relative;
    top: -1px;
  }
  
  li button {
    float: right;
    display: none;
    margin-top: 3px;
  }
  
  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;
    margin-right: 5px;
  }
  
  .todo-footer button {
    float: right;
    margin-top: 5px;
  }
  

2.4、把App.jsx、App.css拆分至各个组件

App.jsx

//导入核心库
import React, { Component } from "react";
import "./App.css";

//引入Header组件
import Header from "./components/Header";
//引入List组件
import List from "./components/List";
//引入Footer组件
import Footer from "./components/Footer";

//创建外壳组件并暴露
export default class App extends Component {
  render() {
    // 把组件hello、welcome导入
    return (
      <div id="root">
        <div className="todo-wrap">
          <Header />
          <List />
          <Footer />
        </div>
      </div>
    );
  }
}

react_staging/src/components/Header/index.jsx、index.css

import React, { Component } from "react";

export default class header extends Component {
  render() {
    return (
      <div className="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" />
      </div>
    );
  }
}

/*header*/
.todo-header input {
    width: 560px;
    height: 28px;
    font-size: 14px;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 4px 7px;
  }
  
  .todo-header input:focus {
    outline: none;
    border-color: rgba(82, 168, 236, 0.8);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
  }
  

react_staging/src/components/Footer/index.jsx、index.css

//src/components/Footer/index.jsx
import React, { Component } from "react";

export default class footer extends Component {
  render() {
    return (
      <div className="todo-footer">
        <label>
          <input type="checkbox" />
        </label>
        <span>
          <span>已完成0</span> / 全部2
        </span>
        <button className="btn btn-danger">清除已完成任务</button>
      </div>
    );
  }
}

/*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;
    margin-right: 5px;
  }
  
  .todo-footer button {
    float: right;
    margin-top: 5px;
  }
  

react_staging/src/components/Item/index.jsx、index.css

import React, { Component } from 'react'

export default class item extends Component {
  render() {
    return (
        <li>
        <label>
          <input type="checkbox" />
          <span>xxxxx</span>
        </label>
        <button className="btn btn-danger" style={{ display: "none" }}>
          删除
        </button>
      </li>
    )
  }
}

/*item*/
li {
    list-style: none;
    height: 36px;
    line-height: 36px;
    padding: 0 5px;
    border-bottom: 1px solid #ddd;
  }
  
  li label {
    float: left;
    cursor: pointer;
  }
  
  li label li input {
    vertical-align: middle;
    margin-right: 6px;
    position: relative;
    top: -1px;
  }
  
  li button {
    float: right;
    display: none;
    margin-top: 3px;
  }
  
  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;
    margin-right: 5px;
  }
  
  .todo-footer button {
    float: right;
    margin-top: 5px;
  }

react_staging/src/components/List/index.jsx、index.css

import React, { Component } from "react";
import Item from "../Item";
export default class list extends Component {
  render() {
    return (
      <ul className="todo-main">
        <Item></Item>
      </ul>
    );
  }
}

  /*main*/
  .todo-main {
    margin-left: 0px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding: 0px;
  }
  
  .todo-empty {
    height: 40px;
    line-height: 40px;
    border: 1px solid #ddd;
    border-radius: 2px;
    padding-left: 5px;
    margin-top: 10px;
  }

2.4、样式结构效果

在这里插入图片描述

四、动态初始化列表

4.1、在App.jsx 初始化状态

export default class App extends Component {
  //初始化状态
  state={todos:[
    {id:'1',name:'吃饭',done:true},
    {id:'2',name:'睡觉',done:true},
    {id:'3',name:'打代码',done:false},
    
  ]}
  render() {

    const {todos}=this.state
    return (
      <div id="root">
        <div className="todo-wrap">
          <Header />
          <List todos={todos}/>
          <Footer />
        </div>
      </div>
    );
  }
}

4.2、通过props在List组件接收数据,App.jsx传给List

import React, { Component } from "react";
import Item from "../Item";
import './index.css'
export default class list extends Component {
  render() {
    const {todos}=this.props
    return (
      <ul className="todo-main">
        {todos.map((todo)=>{
          console.log(todo);
          return <Item key={todo.id} {...todo} />
        })}
      </ul>
    );
  }
}

4.3、通过props在Item组件接收数据,List传给Item,渲染页面

import React, { Component } from 'react'
import './index.css'

export default class item extends Component {
  render() {
   const {name ,done}=this.props
    console.log(this.props);
    return (
        <li>
        <label>
          <input type="checkbox" defaultChecked={done}/>
          <span>{name}</span>
        </label>
        <button className="btn btn-danger" style={{ display: "none" }}>
          删除
        </button>
      </li>
    )
  }
}

4.4、效果

在这里插入图片描述

五、 点击回车添加todo项

5.1、总体思路:

App.jsx传一个addTodo()函数给Header,Header接收到了这个函数,调用这个函数,把键盘监听输入的对象重新传给App.jsx,App.jsx拿到了数据状态,进行状态更新,状态更新,重新调用render,List是子组件,引发了List子组件的重新渲染

5.2、nanoid

为添加的对象自动生成唯一的一个id
在这里插入图片描述

在这里插入图片描述

//准备一个todo对象
    import { nanoid } from "nanoid";
    const todoObject = { id: nanoid(), name: target.value, done: false };

5.3、监听键盘输入,拿到数据,通过App传入的函数,调用返回输入的数据

//react_staging/src/components/Header/index.jsx
import React, { Component } from "react";
import { nanoid } from "nanoid";
import "./index.css";

export default class header extends Component {
  //鼠标事件
  handleKeyUp = (event) => {
    // 判断键盘事件是不是13(13代表回车) 如果按下回车 输出输入框的值
    //解构赋值
    const { keyCode, target } = event;
    if (keyCode !== 13) {
      return;
    } 

    //添加todo的名字不能为空
    if(target.value.trim()===''){
      alert('输入不能为空')
      return
    }
    //准备一个todo对象
    const todoObject = { id: nanoid(), name: target.value, done: false };

    //将todoObject传递给App
    this.props.addTodo(todoObject);
    console.log(todoObject);

    //清空输入
    target.value=''

  };
  render() {
    return (
      <div className="todo-header">
        <input
          type="text"
          onKeyUp={this.handleKeyUp}
          placeholder="请输入你的任务名称,按回车键确认"
        />
      </div>
    );
  }
}

5.4、定义一个函数,传给Header,调用函数返回输入的数据,最后在函数里面更新状态,重新渲染List


//react_staging/src/App.jsx
//导入核心库
import React, { Component } from "react";
import "./App.css";

//引入Header组件
import Header from "./components/Header";
//引入List组件
import List from "./components/List";
//引入Footer组件
import Footer from "./components/Footer";

//创建外壳组件并暴露
export default class App extends Component {
  //初始化状态
  state={todos:[
    {id:'1',name:'吃饭',done:true},
    {id:'2',name:'睡觉',done:true},
    {id:'3',name:'打代码',done:false},
    
  ]}

  addTodo=(todoObject)=>{
    //获取原todos
    console.log('App--todoObject',todoObject);
    const {todos}=this.state
    //追加一个todo
    // todoObject:最新添加的数据
    // ...todos:原来的数据
    const newTodos=[todoObject,...todos]
    console.log('...todos',...todos);
    //更新状态
    this.setState({todos:newTodos})
    

  }
  render() {

    const {todos}=this.state
    return (
      <div id="root">
        <div className="todo-wrap">
          <Header  addTodo={this.addTodo}/>
          <List todos={todos}/>
          <Footer />
        </div>
      </div>
    );
  }
}

5.5、效果

请添加图片描述

六、鼠标移动到todo的效果(高亮和删除按钮)

6.1、总体思路

定义鼠标的移入和移出事件,传给true,false过去给return高阶函数。初始化state的mouse,把闯过来的布尔值更新状态到mouse,通过mouse来设置高亮和按钮的显示与隐藏

import React, { Component } from "react";
import "./index.css";

export default class item extends Component {
  //初始化状态
  state = { mouse: "" };
  //鼠标移入移出事件
  handleMouse = (flag) => {
    return () => {
      //更新mouse状态
      this.setState({ mouse: flag });
    };
  };
  render() {
    const { name, done } = this.props;

    //解构赋值 从state取出mouse
    const { mouse } = this.state;
    return (
      <li
        style={{ backgroundColor: mouse ? "aqua" : "white" }}
        onMouseEnter={this.handleMouse(true)}
        onMouseLeave={this.handleMouse(false)}
      >
        <label>
          <input type="checkbox" defaultChecked={done} />
          <span>{name}</span>
        </label>
        <button
          className="btn btn-danger"
          style={{ display: mouse ? "block" : "none" }}
        >
          删除
        </button>
      </li>
    );
  }
}

请添加图片描述

七、勾选和取消勾选

7.1、总体思路

在App里面定义一个函数去循环判断别的地方传来的id是否相匹配,是的话就修改done的值,更新状态。由于是祖孙传参,需要App传给List这个函数,List把这个函数传给Item。item接到这个函数返回id和更改的done的值给App,App得到一个新的状态,渲染页面

7.2、定义函数等待孙子组件调用函数返回数据修改状态

  //更新更改done的todos
  updateTodo = (id, done) => {
    //获取状态的todos
    const { todos } = this.state;

    //完成取消勾选和勾选的更新之后的状态(更新done)
    const newTodos = todos.map((todoObject) => {
      if (todoObject.id === id) {
        //如果id匹配上了 返回新的对象 done的值被改了
        return { ...todoObject, done };
      } else {
        return todoObject;
      }
    });
    console.log("newTodos", newTodos);
    //更新状态
    this.setState({ todos: newTodos });
  };
 <List todos={todos} updateTodo={this.updateTodo} />

7.3、爷爷组件传给父组件

 const {todos,updateTodo}=this.props
<Item key={todo.id} {...todo} updateTodo={updateTodo}

7.4、父组件传给子组件,子组件接到这个函数返回数据给爷爷组件

  const {id, name, done } = this.props;
  //勾选和取消勾选事件
  handleCheck=(id)=>{
  return(event)=>{
    this.props.updateTodo(id,event.target.checked)
  }
  }
 
 <input type="checkbox" defaultChecked={done} onChange={this.handleCheck(id)}/>

请添加图片描述

八、对props进行类型限制

8.1、导包

在这里插入图片描述

import PropTypes from "prop-types";

8.2、函数的限制

  static propTypes = {
    addTodo: PropTypes.func.isRequired,
  };

8.3、数组的限制

  static propTypes = {
  //数组的限制
    todos:PropTypes.array.isRequired,
   //函数的限制
    updateTodo: PropTypes.func.isRequired,
  };

九、删除一个todo

根据id删除对应的todo,App通过deleteTodo函数根据Item组件传来的id去删除对应的todo,那么App把函数传给List,List把函数传给Item,item接到函数返回id给App,渲染页面

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 11:33:45  更:2022-04-26 11:34:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:03:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码