zoukankan      html  css  js  c++  java
  • React开发入门:以开发Todo List为例

    概述

    起因是,为了做毕设,顺便学点前端,我打算学习React。

    MDN通过一个Todo List App的制作,教导React的知识点。

    这是我在MDN学习React的总结,总结出了一些React开发的基本特性,加上MDN上这个教程的简体中文还没翻译过来,我的总结就更有意义了。

    我的成品放在了Github

    React基本概念

    JSX是什么?

    JSX语法,是长得很像HTML的JavaScript代码,简称:JavaScript语法的类HTML扩展(后面会介绍,它和HTML的区别)

    const header = (
      <header>
        <h1>Mozilla Developer Network</h1>
      </header>
    );
    

    关于它的一些原理:浏览器是无法理解JSX的,JSX语法会在编译的时候,被转换:

    const header = React.createElement("header", null,
      React.createElement("h1", null, "Mozilla Developer Network")
    );
    

    设置React APP

    初始化APP

    npx create-react-app moz-todo-react
    

    应用结构

    moz-todo-react
    ├── README.md
    ├── node_modules
    ├── package.json
    ├── package-lock.json
    ├── .gitignore
    ├── public
    │   ├── favicon.ico
    │   ├── index.html
    │   └── manifest.json
    └── src
        ├── App.css
        ├── App.js
        ├── App.test.js
        ├── index.css
        ├── index.js
        ├── logo.svg
        └── serviceWorker.js
    
    • src是源码
    • public包含了在开发APP过程中,浏览器需要读取的文件;index.html是最重要的文件,React会将src中的源码注入这个文件,是的浏览器能够运行源码;其中有个<title>标签,是应用的便签上显示的应用名

    探索第一个React组件<App />

    在React中,component是一个代表APP某部分的可重用的模块。

    App.js 由三个主要部分组成:顶部的一些 import 语句、中间的App组件和底部的 export 语句。大多数React组件都遵循这种模式。

    // 第一个语句导入React库本身。因为React将我们写入的JSX转换为React.createElement(),所以所有的React组件都必须导入React模块。如果跳过这一步,应用程序将产生一个错误。
    import React from 'react';
    // 注意./被使用在路径的开头,.svg扩展名在路径的结尾——这告诉我们文件是本地的,而且这不是JavaScript文件。
    import logo from './logo.svg';
    // 第三条语句导入了与我们的App组件相关的CSS。
    import './App.css';
    
    // 这个App函数返回一个JSX表达式。这个表达式定义了浏览器渲染到DOM的内容。
    function App() {
      return (
        // 表达式中的某些元素具有属性,这些属性的编写方式与HTML类似,遵循attribute="value"的模式。
        // <div>标签有一个className属性。这与HTML中的class属性相同,但因为JSX是JavaScript,所以我们不能使用class这个词——它是保留的,这意味着JavaScript已经将它用于特定目的,这将在我们的代码中引起问题。
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>
              Hello, World!
            </p>
          </header>
        </div>
      );
    }
    
    // 在App.js文件的最底部,export default App语句使我们的App组件可以被其他模块使用。
    export default App;
    

    index.js

    import React from 'react';
    import ReactDOM from 'react-dom';
    import './index.css';
    import App from './App';
    
    ReactDOM.render(<App />, document.getElementById('root'));
    

    调用React的ReactDOM.render()函数,有2个参数:

    1. 被渲染的组件,在这个例子中是<App />。
    2. 被渲染的DOM元素,在本例中是ID为root的元素。如果你查看public/index.html内部。你会看到这是<body>中的<div>元素。

    变量和props

    JSX中的变量

    注意App.js文件中的这一行:

    <img src={logo} className="App-logo" alt="logo" />
    

    这里,<img />标签的src属性值用花括号括起来。

    JSX就是这样识别变量的。React会识别{logo},知道你是在应用程序的第2行导入logo,然后检索logo文件并渲染它。

    使用自定义变量,定义say Hello的对象subject

    function App() {
      const subject = "React";
      return (
        <div className="App">
          <header className="App-header">
            <img src={logo} className="App-logo" alt="logo" />
            <p>
              Hello, {subject}!
            </p>
          </header>
        </div>
      );
    }
    

    组件props

    • prop是传递到React组件中的任何数据。React props与HTML属性类似。HTML元素有属性,React组件有props。
    • props是在组件调用时直接定义和初始化的,使用与HTML属性相同的语法——prop="value"。(你可以将使用一个组件,看作是调用一个组件的函数,函数的返回值是JSX表达式)
    • 在React中,数据流是单向的:props只能从父组件向下传递到子组件;props是只读的。
    props使用实例

    打开index.js,在调用<App />时,直接定义和初始化props。

    ReactDOM.render(<App subject="Clarice" />, document.getElementById('root'));
    

    在App.js中,接收props。

    function App(props) {
      const subject = props.subject;
      return (
        // return statement
      );
    }
    

    小结(React基本概念)

    • 组件可以import需要的module,且必须在文件底部export自己。
    • 组件函数使用PascalCase命名。(可以理解为,将camelCase的第一个单词首字母大写)
    • 您可以通过将变量放在花括号之间,在JSX中读取它们,比如{so}。
    • 为了不与JavaScript保留字冲突,一些JSX属性与HTML属性不同。例如,HTML中的class转换为JSX中的className。注意,多单词属性是camelCase的。
    • Props就像HTML属性一样,在组件调用时被初始化,并被传递到组件中。

    常用特性(以Todo List App的开发为例)

    迭代(遍历)渲染

    需求:Todo List会由很多待办事项,即Todo item组成。通过迭代渲染,我们可以很优雅的渲染一系列的Todo component。

    通常用来渲染一个JSX对象数组。

    初始化<Todo />数组:

    const taskList = props.tasks.map(task => (
      <Todo id={task.id} name={task.name} completed={task.completed} />
    ));
    

    渲染数组:

    <ul
      role="list"
      className="todo-list stack-large stack-exception"
      aria-labelledby="list-heading"
    >
      {taskList}
    </ul>
    

    唯一key

    • 现在React将我们的待办事项从一个数组中渲染出来,为了正确地渲染它们,React必须记录并正确地区分它们。
    • React试图通过自己的猜测来记录,但是我们可以通过向<Todo />元素传递一个key props来帮助它解决这个问题。
    • key是React管理的一个特殊 props ——你不能将key用于任何其他目的。
    • 可以使用nanoid来生成。。。
    const taskList = props.tasks.map(task => (
        <Todo
          id={task.id}
          name={task.name}
          completed={task.completed}
          key={task.id}
        />
      )
    );
    
    • 被迭代渲染的每个元素,必须被传入一个唯一的key

    处理事件(handling events)

    需求:处理新增Todo事项的表单提交

    在React中,我们直接在JSX中的元素上编写事件处理器:

    <button
      type="button"
      onClick={() => alert("hi!")}
    >
      Say hi!
    </button>
    

    这似乎与最佳实践建议相悖,建议不要在HTML上使用内联事件处理程序,但请记住,JSX实际上是JavaScript的一部分。

    onClick属性的意义:

    • 它使得React,在用户单击按钮时运行一个给定的函数。
    • onClick的camelCase非常重要——JSX不会识别onclick(同样,它已经在JavaScript中用于特定目的,这与标准的onclick处理程序属性相关但又不同)。
    • 在 JSX 中,所有的浏览器事件都遵循这种格式:on + 事件的名称。

    处理表单提交

    function handleSubmit(e) {
      e.preventDefault();
      alert('Hello, world!');
    }
    

    为<form>元素添加一个onSubmit属性,并将其值设置为handleSubmit函数:

    <form onSubmit={handleSubmit}>
    

    回调(callback) props

    需求:在Todo List中,我们需要一个功能:在<Form />中提交新的待办事项,然后展示在<App />中。

    这意味着,我们需要将数据从子组件传递到父组件!

    • 在React应用程序中,交互性很少局限于一个组件:一个组件中发生的事件会影响应用程序的其他部分。
    • 我们不能像使用标准props将数据从子组件传递到父组件那样,将数据从父组件传递到子组件。

    因此,我们无法实现这个需求?

    不,我们可以在<App />中编写一个函数,然后将该函数作为 props 传递给<Form />,该函数将从我们的 <Form /> 中获取一些数据作为输入。

    • 这个函数prop(function-as-a-prop)被称为回调(callback)prop。

    实现:

    1. 在App()函数中,编写函数addTask()

      function addTask(name) {
        alert(name);
      }
      
    2. 将addTask函数作为prop传入<Form />

      <Form addTask={addTask} />
      
    3. 在Form()函数内部的handleSubmit方法中,调用callback prop

      function handleSubmit(e) {
        e.preventDefault();
        props.addTask("Say hello!");
      }
      

    State和useState hook

    前情提要:前面说到,我们能够使用callback prop将数据回传给父组件

    需求:如何保存用户的输入内容呢?用户提交了表单之后,如何清空输入,也就是如何更新内容呢?

    总所周知,软件工程嘛,封装肯定是越private越好,将数据留在和它最相关的地方。

    • 组件自身拥有的数据,称为State。
    • State是React的另一个强大工具,因为组件不仅拥有State,而且可以在以后,使用useState hook更新它。
    • 和State不同,prop是只读的(read-only)!
    • React提供了各种特殊的功能,被称为hook,允许我们为组件提供新的能力,比如state。

    使用React hook前,需要先import:

    import React, { useState } from "react";
    
    • useState()为组件创建一个state,它的唯一参数决定state的初始值。

    • 它返回两个东西:state和一个稍后可用于更新state的函数。

    const [name, setName] = useState('Use hooks!');
    

    文本替换(React 无关)

    需求:动态显示未完成的待办事项

    反单引号字符串拼接+三元表达式

    const tasksNoun = taskList.length !== 1 ? 'tasks' : 'task';
    const headingText = `${taskList.length} ${tasksNoun} remaining`;
    

    应用:

    <h2 id="list-heading">{headingText}</h2>
    
    不准不开心。
  • 相关阅读:
    转:【More Effective C#】Lambda表达式优化
    转:Highcharts图表控件的使用
    C# subString的理解
    转:TimeSpan的用法
    Android学习笔记一:Android基本组件和Activity生命周期
    IIS 反向代理设置
    WebApi 身份认证解决方案:Basic基础认证
    Calling async method synchronously
    C# 公共类
    aspnet-api-versioning
  • 原文地址:https://www.cnblogs.com/iltonmi/p/14473661.html
Copyright © 2011-2022 走看看