[Cubo de la familia React] A través del modo de diseño de componentes, haga una tarea simple (incluido el código fuente)

1. Estructura del proyecto de diseño

Debido a que implementamos todo como un componente funcional, las ideas básicas de diseño son:

1. Cree una nueva carpeta de tareas pendientes en componentes, cree un nuevo index.jsx como componente de tareas pendientes y coloque algunos componentes de tareas pendientes en esta carpeta.

2. Introducir el componente en App.jsx import Todo from './components/Todo'y renderizarlo en render:

import React, { Component } from 'react';
import Todo from './components/Todo';

export default class APP extends Component {
  render() {
    return (
      <div id="root">
        <Todo />
      </div>
    );
  }
}

Segundo, divida los componentes para lograr páginas estáticas

1. Dividimos el componente de tareas pendientes en cuatro partes: Encabezado, Lista, Elemento y Pie de página

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leech, se recomienda guardar la imagen y cargarla directamente (img-oLlmqUDz-1642866764006) (C:\Users\Administrator\AppData\Roaming\Typora\typora -imágenes-de-usuario\ imagen-20220122150045635.png)]

2. Cree cuatro carpetas nuevas: Encabezado, Lista, Elemento y Pie de página en la carpeta de tareas pendientes, y cree nuevos index.jsx e index.css en ellas respectivamente.

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leech, se recomienda guardar la imagen y cargarla directamente (img-o34tDyiN-1642866764012) (C:\Users\Administrator\AppData\Roaming\Typora\typora -imágenes-de-usuario\imagen-20220122153547168.png)]

3. Complete los recursos estáticos en jsx y css respectivamente

Todo文件夹

/*Todo/index.jsx*/
import React, { Component } from 'react';
import './index.css'
import Footer from './Footer';
import Header from './Header';
import List from './List';

export default class Todo extends Component {
  render() {
    return (
      <div id="root">
        <div className="todo-container">
          <div className="todo-wrap">
            <Header />
            <List />
            <Footer />
          </div>
        </div>
      </div>
    );
  }
}
/*Todo/index.css--公用样式*/
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文件夹

/*Header/index.jsx*/

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

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

/*header/index.css*/
.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);
}

List文件夹

/*List/index.jsx*/
import React, { Component } from 'react';
import Item from '../Item';
import './index.css'

export default class List extends Component {
  render() {
    return (
      <ul className="todo-main">
        <Item />
      </ul>
    );
  }
}
/*List/index.css*/
.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文件夹

/*Item/index.jsx*/
import React, { Component } from 'react';
import './index.css'

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

/*Item/index.css*/
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文件夹

/*Footer/index.jsx*/
import React, { Component } from 'react';
import './index.css'

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/index.css*/
.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;
}

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leech, se recomienda guardar la imagen y cargarla directamente (img-CbURRJfM-1642866764015) (C:\Users\Administrator\AppData\Roaming\Typora\typora -imágenes-de-usuario\ imagen-20220122155516577.png)]

3. Función 1_Añadir información de la tarea a la primera línea

因为涉及到兄弟组件之间的传值,所以我们这里需要通过与父组件的Props传值方法

1. Primero, configure el número analógico en el componente principal (Todo/index.jsx) y páselo a la Lista de componentes secundarios.

/*Todo/index.jsx*/
import React, { Component } from 'react';
import './index.css'
import Footer from './Footer';
import Header from './Header';
import List from './List';

export default class APP extends Component {
  state = {
    todos: [{
      id: '001',
      name: '吃饭',
      done: true
    }, {
      id: '002',
      name: '睡觉',
      done: true
    }]
  }
  render() {
    const { todos } = this.state
    return (
      <div id="root">
        <div className="todo-container">
          <div className="todo-wrap">
            <Header />
            <List todos={todos} />
            <Footer />
          </div>
        </div>
      </div>
    );
  }
}

2. El componente List recibe los Props pasados ​​desde el componente principal Todo y transmite el valor al subcomponente Item de List para su visualización.

/*List/index.jsx*/
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((item) => {
            return <Item key={item.id} {...item} />
          })
        }
      </ul>
    );
  }
}

3. El componente Item recibe los accesorios de la lista del componente principal y los muestra.

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((item) => {
            return <Item key={item.id} {...item} />
          })
        }
      </ul>
    );
  }
}

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leech, se recomienda guardar la imagen y cargarla directamente (img-MKw1rR58-1642866764017) (C:\Users\Administrator\AppData\Roaming\Typora\typora -imágenes-de-usuario\ imagen-20220122160628490.png)]

4. El componente principal Todo obtiene los datos agregados en el encabezado del componente secundario a través de la función de transferencia de accesorios (addTodo)

import React, { Component } from 'react';
import './index.css'
import Footer from './Footer';
import Header from './Header';
import List from './List';

export default class APP extends Component {
  state = {
    todos: [{
      id: '001',
      name: '吃饭',
      done: true
    }, {
      id: '002',
      name: '睡觉',
      done: true
    }]
  }
  //用于添加一个todo,参数为todo对象
  addTodo = (todoObj) => {
    let { todos } = this.state;
    //将输入的内容放到数组第一位
    todos.unshift(todoObj);
    //修改状态
    this.setState(todos)
  }
  render() {
    const { todos } = this.state
    return (
      <div id="root">
        <div className="todo-container">
          <div className="todo-wrap">
            <Header addTodo={this.addTodo} />
            <List todos={todos} />
            <Footer />
          </div>
        </div>
      </div>
    );
  }
}

5. El componente Header ejecuta la función pasada desde el componente principal Todo al escuchar la "tecla Enter" para actualizar el estado

nanoid的作用是生成唯一的值,主要为了给元素的key进行赋值

import React, { Component } from 'react';
//类似于uuid,生成唯一的id
import { nanoid } from 'nanoid';
import './index.css'

export default class Header extends Component {
  handleKeyUp = (event) => {
    const { keyCode, target } = event;
    if (keyCode === 13) {
      //这里还需要判断空格的多种情况(正则)
      if (target.value !== "") {
        const newTodo = { id: nanoid(), name: target.value, done: false };
        this.props.addTodo(newTodo);
        //清空输入的内容
        target.value = ""
      }
    }
  }
  render() {
    return (
      <div className="todo-header">
        <input type="text" placeholder="请输入你的任务名称,按回车键确认" onKeyUp={this.handleKeyUp} />
      </div>
    )
  }
}

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo anti-leech, se recomienda guardar la imagen y cargarla directamente (img-PzOxbJRp-1642866764019) (D:\desktop\GIF1.gif)]

4. Función 2_ Cuando el mouse se mueve en la lista, se resaltará y se mostrará el botón "Eliminar".

此功能主要通过鼠标的移入移出事件进行判断,高亮的功能可以通过css中的:hover实现,本案例使用React中的方法

1. Definir el estado del componente

state = { mouseType: false }

2. Defina los eventos de movimiento de entrada y salida del mouse, verdadero significa movimiento hacia adentro, falso significa movimiento hacia afuera, y el color de fondo y la visualización se representan de acuerdo con el valor de mouseType en el estado. (Esto se implementa usando el método curry de funciones de orden superior)

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

export default class Item extends Component {
  state = { mouseType: false }
  //鼠标移入移出事件
  handleMouse = (mouseType) => {
    return () => {
      this.setState({ mouseType })
    }
  }
  render() {
    const { name, done } = this.props
    return (
      <div>
        <li style={
   
   { backgroundColor: this.state.mouseType ? '#ddd' : '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: this.state.mouseType ? 'block' : 'none' }}>删除</button>
        </li>
      </div>
    );
  }
}

inserte la descripción de la imagen aquí

Cinco, función 3_ haciendo clic en la casilla de verificación, cambie el hecho en el estado

1. Porque para implementar el componente secundario Item para modificar el estado en el componente principal Todo de la lista del componente principal, es necesario definir la función updateTodo en el componente principal Todo y pasarla al componente Item a través de accesorios.

/*Todo/index.jsx*/

//用于更新todo,参数为id
  updateTodo = (id, done) => {
    const { todos } = this.state;
    let newTodos = todos.map((item) => {
      if (item.id === id) {
        return { ...item, done }
      } else {
        return item
      }
    })
    this.setState({ todos: newTodos })
  }
<List todos={todos} updateTodo={this.updateTodo} />

List/index.jsx

<Item key={item.id} {...item} updateTodo={updateTodo} />

2. En el componente Elemento, agregue un evento onChange a la casilla de verificación y pase la identificación de la tarea pendiente correspondiente a la casilla de verificación seleccionada actualmente como parámetro para handleCheck

<input type="checkbox" defaultChecked={done} onChange={this.handleCheck(id)} />

3. En la función handleCheck, ejecute el id y el checkbox actual como parámetros de updateTodo

//勾选或者取消勾选
handleCheck = (id) => {
  return (event) => {
    this.props.updateTodo(id, event.target.checked)
  }
}

4. Introduzca la biblioteca prop-types para limitar los accesorios de cada componente (vea cómo usar: npm.com prop-types )

  /*以Item/index.jsx为例*/
  //对props进行限制
  static propTypes = {
    updateTodo: PropTypes.func.isRequired,
    done: PropTypes.bool.isRequired
  }

inserte la descripción de la imagen aquí

Seis, función 4_ eliminar una tarea pendiente

1. La lógica es la misma que la forma de modificar hecho Primero, en el componente Elemento, vincule onClick al botón Eliminar

<button onClick={this.handleDel(id)} className="btn btn-danger" style={
   
   { display: this.state.mouseType ? 'block' : 'none' }}>删除</button>

2. Defina la función handleDel y ejecute la función deleteTodo pasada desde el componente Todo

  //删除一个todo
  handleDel = (id) => {
    return () => {
      this.props.deleteTodo(id)
    }
  }

3. Defina la función deleteTodo en el componente Todo, pásela al subcomponente List y luego pásela al subcomponente Item a través del componente List.

Todo/index.jsx

//删除指定todo对象
  deleteTodo = (id) => {
    const { todos } = this.state;
    const newTodos = todos.filter(item => {
      return item.id !== id
    })
    this.setState({ todos: newTodos })
  }
<List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo} />

List/index.jsx

<Item key={item.id} {...item} updateTodo={updateTodo} deleteTodo={deleteTodo} />

7. Función 5_ Realice la función de seleccionar todo y borrar en la parte inferior

1. Primero realiza las estadísticas del número completado y todos los números

Footer/index.jsx

const { todos } = this.props
//已完成数
const doneCount = todos.reduce((pre, current) => {
  return current.done === true ? ++pre : pre
}, 0)
<span>
  <span>已完成{doneCount}</span> / 全部{todos.length}
</span>

2. Implementar la función de seleccionar todo

Footer/index.jsx

<input type="checkbox" checked={doneCount === todos.length && todos.length !== 0 ? true : false} onChange={this.handleCheckAll} />
//全选
handleCheckAll = (event) => {
	this.props.checkAllTodo(event.target.checked)
}

3. Debido a que desea cambiar el estado de los datos, debe definir una función en el componente principal Todo y pasarla a través de accesorios.

Todo/index.jsx

//全选
checkAllTodo = (done) => {
    const { todos } = this.state;
    let newTodos = todos.map(item => {
      return { ...item, done: done }
    })
    this.setState({ todos: newTodos })
}
<Footer todos={todos} checkAllTodo={this.checkAllTodo} />

4. Realice la función de compensación completada

Footer/index.jsx

<button className="btn btn-danger" onClick={this.handleClear}>清除已完成任务</button>
//清除已完成任务
handleClear = () => {
	this.props.clearAllDone()
}

5. Debido a que desea cambiar el estado de los datos, debe definir una función en el componente principal Todo y pasarla a través de accesorios.

Todo/index.jsx

//清除所有已完成
clearAllDone = () => {
    const { todos } = this.state;
    const newTodos = todos.filter(item => {
      return item.done === false
    })
    this.setState({ todos: newTodos })
}
<Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone} />

inserte la descripción de la imagen aquí

8. Resumen del caso

1. Dividir componentes e implementar componentes estáticos Nota: la escritura de className y style

2. Lista de inicialización dinámica, cómo determinar en qué estado del componente colocar los datos

​ Un componente usa: ponerlo en su propio estado

Uso de algunos componentes: colóquelos en su estado de componente principal común (oficialmente llamado promoción estatal )

3. En cuanto a la comunicación entre padre e hijo:

(1) [Componente principal] pasa datos a [componente secundario] a través de accesorios

(2) [Componente secundario] pasa datos a [componente principal]: pasa a través de accesorios, lo que requiere que [componente principal] pase una función a [componente secundario] por adelantado

4. Preste atención a la diferencia entre defaultChecked y Checked, similar a defaultValue y Value

5. ¿Dónde está el estado, dónde está el método de funcionamiento del estado?

Dirección de origen: https://gitee.com/daiwanghao/react-family-bucket.git

Para ver el contenido de las tareas realizadas por React arriba, preste atención a la columna " Cubo de la familia React ".
Compartiré los problemas comunes en mis proyectos habituales y el conocimiento de la prueba escrita y la entrevista contigo en CSDN, y progresaremos juntos.

Supongo que te gusta

Origin blog.csdn.net/weixin_46318413/article/details/122646051
Recomendado
Clasificación