00-React 集合

91 阅读36分钟

React 介绍

做什么

React 主要是用来操作 DOM 呈现页面(将数据渲染成 HTML 的开元 JavaScript 库)

谁开发

Facebook

为什么学

  1. 原生 JS 操作 DOM 繁琐且效率低(DOM-API)
  2. 使用 JS 直接操作 DOM,浏览器会打大量的重绘重排
  3. 原生 JS 没有组件化编码方案,代码复用率很低

React 的特点

  1. 采用组件化模式,声明式代码,提高开发效率及组件复用率
  2. 在 React Native 中可以使用 React 语法进行移动端开发
  3. 使用虚拟 DOM+优秀的 diff 算法,尽量减少与真是 DOM 的交互

学习 React 之前要掌握的 JS 基础知识

  1. this
  2. class
  3. ES6
  4. npm
  5. 原型及原型链
  6. 数组常用方法
  7. 模块话

babel

ES6 ---> ES5 JSX ---> JS

React 学习

React 复习

01-类的基本知识

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>
  <body>
    <script type="text/JavaScript">
      /**
       * 总结
       * 1. 类的实例不是必须写的,需要对实例进行一些初始化操作挥着添加属性的属性需要写
       * 2. 如果A类继承了B类,且A类写了构造器,那么A类构造器中 super() 必须要调用
       * 3. 类中所定义的方法,都是放在类的原型对象上,供实例去使用
       */
      class person {
        // 构造器方法
        constructor(name, age) {
          // 构造器中的this-->类的实例对象(也就是谁new的)
          this.name = name;
          this.age = age;
        }
        // 一般方法
        speak() {
          // speak 方法放在了 ---> 类的原型对象上; 供实例使用
          // 通过person实例调用时候,speak的this就是person实例
          console.log("我叫", this.name, "我年龄是", this.age);
        }
      }
      const p1 = new person("小张", 100);
      const p2 = new person("小红", 101);
      console.log(p1);  // person {name: '小张', age: 100}
      console.log(p2);  // person {name: '小红', age: 101}
      console.log(p1.speak()); //我叫 小张 我年龄是 100
      //   创建一个student类继承于person
      class student extends person {
        constructor(name, age, grade) {
          super(name, age);
          this.grade = grade;
        }
        speak() {
          console.log(
            "我叫",
            this.name,
            "我年龄是",
            this.age,
            "我上年纪",
            this.age
          );
        }
        study() {
          // study 方法放在了 ---> 类的原型对象上; 供实例使用
          // 通过 student 实例调用时候, study 的 this 就是 student 实例
        }
      }
      const s1 = new student("小白", 10, "六年级");
      const s2 = new student("小黑", 11, "五年级");
      s1.speak(); // 我叫 小白 我年龄是 10 我上年纪 10
      console.log(s1);  // student {name: '小白', age: 10, grade: '六年级'}
      console.log(s2);  // student {name: '小黑', age: 11, grade: '五年级'}
    </script>
  </body>
</html>

02-原生事件绑定

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button id="btn1">按钮1</button>
    <button id="btn2">按钮2</button>
    <button id="btn3" onclick="demo3()">按钮3</button>
    <script>
      const btn1 = document.getElementById("btn1");
      const btn2 = document.getElementById("btn2");
      const btn3 = document.getElementById("btn3");
      btn1.addEventListener("click", () => {
        alert("点击了按钮1");
      });
      btn2.onclick = () => {
        alert("点击了按钮2");
      };
      function demo3(params) {
        alert("点击了按钮3");
      }
    </script>
  </body>
</html>

03-类中方法 this 的指向

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <script type="text/javascript">
      class person {
        constructor(name, age) {
          this.name = name;
          this.age = age;
        }
        speak() {
          console.log(this);
        }
      }
      const p1 = new person("tom", 18);
      const x = p1.speak;
      x(); // undefined
      p1.speak(); // person {name: 'tom', age: 18}

      //   function demo(params) {
      //     "use strict"; // this就undefined了
      //     console.log(this); // window
      //   }
      //   demo();
    </script>
  </body>
</html>

04-展开运算符

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <script>
      let arr1 = [1, 3];
      let arr2 = [2, 4];

      // 应用1:展开数组
      console.log(...arr1); // 1 3

      // 应用2: 连接数组
      let arr3 = [...arr1, ...arr2];
      console.log(arr3); // (4) [1, 3, 2, 4]

      // 应用3: 配合函数使用
      function sum(...numbers) {
        console.log(numbers); // [1]
        let sum = numbers.reduce((pre, cur) => {
          return pre + cur;
        }, 0);
        return sum;
      }
      console.log(sum(1, 2, 3, 4, 5)); // 15

      // 应用4: 构造字面量对象的时候使用展开语法
      let person = { name: "123", value: "456" };
      let person2 = { ...person }; // 这个写法 {...} 是ES6字面量形式,是被允许的
      // console.log(...person2);    // 展开运算符不能展开对象
      console.log(person2); // {name: '123', value: '456'}

      // 应用4: 合并对象
      let person3 = { ...person, address: "999" };
      console.log(person3); // {name: '123', value: '456', address: '999'}
    </script>
  </body>
</html>

05-高阶函数和函数柯里化

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 高阶函数: 如果一个函数符合下面两个规范中的任何一个,那么该函数就是高阶函数
       *      1. 若A函数,接收的参数是一个函数,那么A函数就称为高阶函数
       *      2. 若A函数,调用的返回值依然是一个函数,那么A函数就称为高阶函数
       * 常见的高阶函数
       *      1. Promise    new Promise(()=>{})
       *      2. setTimeout
       *      3. arr.map 等等
       *
       *
       * 函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码方式
       */
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        state = {
          username: "",
          password: "",
        };
        // 提交表单
        handleSubmit = (event) => {
          event.preventDefault(); // 可以阻止表单的默认跳转
          const { username, password } = this.state;
          console.log(username, password);
        };
        // 保存表单项的数据到状态中
        saveFormData = (dataType) => {
          return (event) => {
            this.setState({ [dataType]: event.target.value });
          };
        };
        render() {
          return (
            <form onSubmit={this.handleSubmit}>
              用户名:{" "}
              <input
                onChange={this.saveFormData("username")}
                type="text"
                name="username"
              />
              密码: <input
                onChange={this.saveFormData("password")}
                type="password"
                password="password"
              />
              <button>登录</button>
            </form>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

06-对象相关的知识

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <script>
      let a = "name";
      let obj = {};
      obj[a] = a;
    </script>
  </body>
</html>

07-演示函数柯里化

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>

  <body>
    <script>
      // function sum(a, b, c) {
      //     return a + b + c
      // }
      // const result = sum(1, 2, 3)

      // 函数柯里化
      function sum(a) {
        return (b) => {
          return (c) => {
            return a + b + c;
          };
        };
      }
      const result = sum(1)(2)(3);
      console.log(result);
    </script>
  </body>
</html>

React 入门

01-hello_react

demo

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01_hello_react</title>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建虚拟dom
      const VDOM = <h1>01_hello_react</h1>; // 此处不写引号,因为是jsx可以混着写
      // 2. 渲染虚拟dom到页面
      ReactDOM.render(VDOM, document.getElementById("test"));
    </script>
  </body>
</html>

02-虚拟 dom 的两种创建方式

2.1-使用 js 创建虚拟 dom

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01_使用js创建虚拟dom</title>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/javascript">
      // 1. 创建虚拟dom (使用这个方法仅限于简单的dom结构)
      const VDOM = React.createElement(
        "h1",
        { id: "title" },
        React.createElement("span", {}, "01_使用js创建虚拟dom")
      );
      // 2. 渲染虚拟dom到页面
      ReactDOM.render(VDOM, document.getElementById("test"));
    </script>
  </body>
</html>

2.2-使用 jsx 创建虚拟 dom

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>02_使用jsx创建虚拟dom</title>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建虚拟dom
      const VDOM = (
        <h1 id="title">
          <span>02_使用jsx创建虚拟dom</span>
        </h1>
      ); // 此处不写引号,因为是jsx可以混着写
      // 2. 渲染虚拟dom到页面
      ReactDOM.render(VDOM, document.getElementById("test"));
    </script>
  </body>
</html>

2.3-虚拟 DOM 和真实 DOM

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>03_虚拟DOM和真实DOM</title>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建虚拟dom
      const VDOM = (
        <h1 id="title">
          <span>03_虚拟DOM和真实DOM</span>
        </h1>
      );
      /**
       * 1. VDOM本质就是 Object 对象
       * 2. 虚拟DOM比较轻, 属性很少,虚拟DOM仅仅React内部使用,无需那么多的属性
       * 3. 真实DOM比较重, 属性很多
       * 4. 虚拟DOM最终会被React转为真实dom呈现到页面上
       */
      console.log("虚拟DOM", VDOM); //虚拟DOM Object
      const TDOM = document.getElementById("test");
      console.log("真实DOM", TDOM); //真实DOM <div id="test"></div>
      console.log(VDOM instanceof Object); // true
      // 2. 渲染虚拟dom到页面
      ReactDOM.render(VDOM, TDOM);
    </script>
  </body>
</html>

03-JSX

全名 JavaScript XML

3-1.JSX 语法规则


1. 定义虚拟 dom 的使用不要使用引号
2. 标签重混入 js 表达式的时候要用{}
 一定要区分JS语句和JS表达式
 1. 表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方
  下面这些是表达式
    1. a
    2. a+b
    3. demo(1)
    4. arr.map()
    5. function test(){}
 2. 语句(代码)
  下面这些是语句(代码)
    1. if(){}
    2. for(){}
    3. switch(){}
3. 样式的类名不要用 class,要用 className
4. 内联样式要用 style={{}} 的形式,并且需要大驼峰 fontSize
5. 只能有一个根标签
6. 标签必须要闭合
7. 标签首字母
   7.1 如果是小写字母开头,则就转为 html 的同名元素,如果没有对应的就会报错
   7.2 如果是大写字母开头,React 就去渲染对应的组件,如果组件没有定义那么就报错
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>03_JSX语法规则</title>
    <style>
      .title {
        width: 200px;
        height: 200px;
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * JSX语法规则
       * 1. 定义虚拟dom的使用不要使用引号
       *    一定要区分JS语句和JS表达式
       *    1. 表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方
       *     下面这些是表达式
       *       1. a
       *       2. a+b
       *       3. demo(1)
       *       4. arr.map()
       *       5. function test(){}
       *    2. 语句(代码)
       *     下面这些是语句(代码)
       *       1. if(){}
       *       2. for(){}
       *       3. switch(){}
       * 2. 标签重混入js表达式的时候要用{}
       * 3. 样式的类名不要用class,要用className
       * 4. 内联样式要用 style={{}} 的形式,并且需要大驼峰 fontSize
       * 5. 只能有一个根标签
       * 6. 标签必须要闭合
       * 7. 标签首字母
       *  7.1 如果是小写字母开头,则就转为html的同名元素,如果没有对应的就会报错
       *  7.2 如果是大写字母开头,React就去渲染对应的组件,如果组件没有定义那么就报错
       */
      const id = "id";
      const data = "03_JSX语法规则";
      // 1. 创建虚拟dom
      const VDOM = (
        <div>
          <h1 id={id.toLowerCase()} className="title">
            <span>{data.toLowerCase()}</span>
          </h1>
          <input type="text"></input>
        </div>
      );
      // 2. 渲染虚拟dom到页面
      ReactDOM.render(VDOM, document.getElementById("test"));
    </script>
  </body>
</html>

3-2.jsx 练习

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>jsx练习</title>
    <style>
      .title {
        width: 200px;
        height: 200px;
        background-color: yellow;
      }
    </style>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 一定要区分JS语句和JS表达式
       *  1. 表达式: 一个表达式会产生一个值,可以放在任何一个需要值的地方
       *   下面这些是表达式
       *     1. a
       *     2. a+b
       *     3. demo(1)
       *     4. arr.map()
       *     5. function test(){}
       * 2. 语句(代码)
       *   下面这些是语句(代码)
       *     1. if(){}
       *     2. for(){}
       *     3. switch(){}
       */
      const data = ["angular", "react", "vue"];
      const data2 = data.map((el) => <li key={el}>{el}</li>);
      // 1. 创建虚拟dom
      const VDOM = (
        <div>
          <h1>前端框架</h1>
          <ul>{data2}</ul>
        </div>
      );
      // 2. 渲染虚拟dom到页面
      ReactDOM.render(VDOM, document.getElementById("test"));
    </script>
  </body>
</html>

React 面向组件编程

1-基本理解和使用

1.1-函数式组件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      function MyComponent(params) {
        console.log(this); // undefined  --->  是因为 babel 编译后是严格模式,那么this就不能指向window了,
        return <h2>我是用函数定义的组件(适用于简单组件的定义)</h2>;
      }
      // 2. 渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test"));
      /**
       * 执行了 ReactDOM.render(<MyComponent />, document.getElementById("test"));
       * 1. React会解析组件标签,找MyComponent组件
       * 2. 发现组件是函数定义的,那么就调用这个函数,
       * 3. 将返回的虚拟dom转换成真实dom.呈现在页面上
       */
    </script>
  </body>
</html>

1.2-类式组件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 类式组件
      class MyComponent extends React.Component {
        render() {
          // render 方法放在了 ---> 类 MyComponent 的原型对象上; 供实例使用
          // render 的this是谁  --->  MyComponent 的实例对象
          console.log(this); // MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, _reactInternalFiber: FiberNode, …}
          return <h1>我是类定义的组件,适用于复杂组件</h1>;
        }
      }
      // 2. 渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test"));
      /**
       * 执行了 ReactDOM.render(<MyComponent />, document.getElementById("test"));
       * 1. React会解析组件标签,找MyComponent组件
       * 2. 发现组件是函数定义的,那么就new出来该类的实例,并通过该实例调用到原型的render方法,
       * 3. 将返回的虚拟dom转换成真实dom.呈现在页面上
       */
    </script>
  </body>
</html>

02-state

2.1-state 基础使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
  </head>
  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建组件
      class Weather extends React.Component {
        // constructor 构造器执行几次?-->new 几次就会执行几次
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = {
            isHot: true,
            wind: "微风",
          };
          // 这样处理是为了解决静态方法内无法拿到类的this,解决this指向的问题
          this.changeWeather = this.changeWeather.bind(this);
        }
        /**
         * changeWeather放在哪里?---Watcher的原型对象上,供实例使用
         * 由于changeWeather方法作为onClick的回调,所以不是实例调用的而是直接调用的
         * 类中的方法默认开启了严格模式,所以changeWeather里面的this是undefined
         *
         */
        //  changeWeather 执行几次? ---> 点几次调用几次
        changeWeather() {
          const isHot = this.state.isHot;
          // state里面的状态里面的数据要修改需要使用 setState 方法
          // this.state.isHot = !isHot   // 错误的写法
          this.setState({
            isHot: !isHot,
          });
        }
        // render 执行几次?--> 执行 n+1 次, n 是状态更新的次数,每次 state 更新, render 都会执行
        render() {
          // 读取状态
          const { isHot, wind } = this.state;
          return (
            <div>
              <button onClick={this.changeWeather}>改变天气</button>
              <h1>
                今天的天气好: {isHot ? "热" : "冷"},{wind}
              </h1>
            </div>
          );
        }
      }
      // 2. 渲染组件到页面
      ReactDOM.render(<Weather />, document.getElementById("test"));
    </script>
  </body>
</html>

2.2-state 简写

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title></title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      class Weather extends React.Component {
        // 初始化状态
        state = { isHot: true, wind: "微风" };

        /**
         * 自定义方法:---以后都成 赋值语句的形式+箭头函数
         * this执行就会到了watcher了
         * 这样的写法就是把 changeWeather 放在自身,而不是放在原型函数上
         */
        changeWeather = () => {
          console.log(this); // Weather {props: {…}, context: {…}, refs: {…}, updater: {…}, state: {…}, …}
          const isHot = this.state.isHot;
          this.setState({ isHot: !isHot });
        };
        render() {
          const { isHot, wind } = this.state;
          return (
            <div>
              <h1 onClick={this.changeWeather}>
                今天的天气好: {isHot ? "热" : "冷"},{wind}
              </h1>
            </div>
          );
        }
      }
      // 2. 渲染组件到页面
      ReactDOM.render(<Weather />, document.getElementById("test"));
    </script>
  </body>
</html>

03-props

3.1-props 基本使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        render() {
          return (
            <ul>
              <li>姓名{this.props.name}</li>
              <li>年龄{this.props.age}</li>
              <li>性别{this.props.sex}</li>
            </ul>
          );
        }
      }
      // 2. 渲染组件到页面
      ReactDOM.render(
        <MyComponent name="tom" age="12" sex="男" />,
        document.getElementById("test")
      );
    </script>
  </body>
</html>

3.2-对 props 进行限制

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script src="../React-js/prop-types.js"></script>

    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // props是只读的
        render() {
          return (
            <ul>
              <li>姓名{this.props.name}</li>
              <li>年龄{this.props.age}</li>
              <li>性别{this.props.sex}</li>
            </ul>
          );
        }
      }
      // 对标签属性进行必要性的限制
      MyComponent.propTypes = {
        name: PropTypes.string.isRequired, // 限制姓名为字符串且必填
        name: PropTypes.string, // 限制必须字符串
        age: PropTypes.number, // 限制必须数字
        speak: PropTypes.func, // 限制必须为函数
      };
      // 指定属性的默认值
      MyComponent.defaultProps = {
        sex: "不男不女",
        age: 18,
      };
      const info = { name: "123", age: 13 };
      ReactDOM.render(
        <MyComponent {...info} speak={speak} />,
        document.getElementById("test1")
      );
      function speak(params) {
        console.log("我说话了");
      }
    </script>
  </body>
</html>

3.3-props 简写

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script src="../React-js/prop-types.js"></script>

    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 对标签属性进行必要性的限制
        static propTypes = {
          name: PropTypes.string.isRequired, // 限制姓名为字符串且必填
          name: PropTypes.string, // 限制必须字符串
          age: PropTypes.number, // 限制必须数字
          speak: PropTypes.func, // 限制必须为函数
        };
        // 指定属性的默认值
        static defaultProps = {
          sex: "不男不女",
          age: 18,
        };
        // props是只读的
        render() {
          return (
            <ul>
              <li>姓名{this.props.name}</li>
              <li>年龄{this.props.age}</li>
              <li>性别{this.props.sex}</li>
            </ul>
          );
        }
      }

      const info = { name: "123", age: 13 };
      ReactDOM.render(
        <MyComponent {...info} speak={speak} />,
        document.getElementById("test1")
      );
      function speak(params) {
        console.log("我说话了");
      }
    </script>
  </body>
</html>

3.4-props 在构造函数中情况

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script src="../React-js/prop-types.js"></script>

    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // props是只读的
        render() {
          return (
            <ul>
              <li>姓名{this.props.name}</li>
              <li>年龄{this.props.age}</li>
              <li>性别{this.props.sex}</li>
            </ul>
          );
        }
      }
      const info = { name: "123", age: 13 };
      ReactDOM.render(
        <MyComponent {...info} speak={speak} />,
        document.getElementById("test1")
      );
      function speak(params) {
        console.log("我说话了");
      }
    </script>
  </body>
</html>

3.5-props 在函数值组件中使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 引入prop-types,用于对组件标签属性进行限制 -->
    <script src="../React-js/prop-types.js"></script>

    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      function MyComponent(props) {
        const { name, age, sex } = props;
        return (
          <ul>
            <li>姓名{name}</li>
            <li>年龄{age}</li>
            <li>性别{sex}</li>
          </ul>
        );
      }
      // 对标签属性进行必要性的限制
      MyComponent.propTypes = {
        name: PropTypes.string.isRequired, // 限制姓名为字符串且必填
        name: PropTypes.string, // 限制必须字符串
        age: PropTypes.number, // 限制必须数字
        speak: PropTypes.func, // 限制必须为函数
      };
      // 指定属性的默认值
      MyComponent.defaultProps = {
        sex: "不男不女",
        age: 18,
      };
      const info = { name: "123", age: 13 };
      ReactDOM.render(
        <MyComponent {...info} />,
        document.getElementById("test1")
      );
    </script>
  </body>
</html>

04-refs

4.1-字符串形式 refs 的基本使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 字符串的refs存在效率上的问题,不推荐使用
       */
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 展示左侧输入框的数据
        showData = () => {
          const { input1 } = this.refs;
          let value1 = input1.value;
          console.log(value1);
        };
        // 展示右侧输入框的数据
        showData2 = () => {
          const { input2 } = this.refs;
          let value2 = input2.value;
          console.log(value2);
        };
        render() {
          return (
            <div>
              <input
                ref="input1"
                type="text"
                placeholder="点击按钮提示数据"
              ></input>
              <button onClick={this.showData}>点我提示左侧的数据</button>
              <input
                ref="input2"
                onBlur={this.showData2}
                type="text"
                placeholder="失去焦点提示数据"
              ></input>
            </div>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

4.2-回调形式 refs 的基本使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 字符串的refs存在效率上的问题,不推荐使用
       */
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 展示左侧输入框的数据
        showData = () => {
          const { input1 } = this;
          let value1 = input1.value;
          alert(value1);
        };
        // 展示右侧输入框的数据
        showData2 = () => {
          const { input2 } = this;
          let value2 = input2.value;
          alert(value2);
        };
        render() {
          return (
            <div>
              <input
                ref={(c) => (this.input1 = c)}
                type="text"
                placeholder="点击按钮提示数据"
              ></input>
              <button onClick={this.showData}>点我提示左侧的数据</button>
              <input
                ref={(c) => (this.input2 = c)}
                onBlur={this.showData2}
                type="text"
                placeholder="失去焦点提示数据"
              ></input>
            </div>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

4.3-回调形式 refs 的回调执行次数

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 字符串的refs存在效率上的问题,不推荐使用
       */
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        state = { isHot: true };
        // 展示左侧输入框的数据
        showData = () => {
          const { input1 } = this;
          let value1 = input1.value;
          alert(value1);
        };
        changeWeather = () => {
          const { isHot } = this.state;
          this.setState({
            isHot: !isHot,
          });
        };
        saveInput = (c) => {
          this.input1 = c;
          console.log(c);
        };
        render() {
          const { isHot } = this.state;
          return (
            <div>
              <h2>今天天气很{isHot ? "热" : "冷"}</h2>
              {/*这种写法有个问题,就是更新时候,这个console.log(c)会执行两次,第一次c是null,第二才是这个dom结构*/}
              {/*<input ref={c => { this.input1 = c; console.log(c); }} type="text" placeholder="点击按钮提示数据"></input>*/}

              {/*这种写法是做好的,只会执行一次,但是没必要*/}
              <input
                ref={this.saveInput}
                type="text"
                placeholder="点击按钮提示数据"
              ></input>
              <button onClick={this.showData}>点我提示左侧的数据</button>
              <button onClick={this.changeWeather}>点我切换天气</button>
            </div>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

4.4-createRef 形式 refs 的基本使用

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        /**
         * React.createRef调用后返回䘝容器,该容器可以存储被ref所标识的节点
         * 该容器是专人专用,只能存一个节点
         */
        myRef1 = React.createRef();
        myRef2 = React.createRef();
        // 展示左侧输入框的数据
        showData = () => {
          alert(this.myRef1.current.value);
        };
        // 展示右侧输入框的数据
        showData2 = () => {
          alert(this.myRef2.current.value);
        };
        render() {
          return (
            <div>
              <input
                ref={this.myRef1}
                type="text"
                placeholder="点击按钮提示数据"
              ></input>
              <button onClick={this.showData}>点我提示左侧的数据</button>
              <input
                ref={this.myRef2}
                onBlur={this.showData2}
                type="text"
                placeholder="失去焦点提示数据"
              ></input>
            </div>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

4.5-.事件处理

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        /**
         * 1. 通过 onXxx 属性指定事件处理函数(注意大小写)
         *   1.1 React 使用的是自定义(合成)事件,而不是使用的原生DOM的事件 --- 为了更好的兼容性
         *   1.2 React 中的事件是通过事件委托方式处理的(委托给组件的最外层) --- 为了高效
         * 2. 通过 event.target 得到发生事件的 DOM 元素 --- 不要过度使用 ref
         */

        //创建ref容器
        myRef1 = React.createRef();
        // 展示左侧输入框的数据
        showData = () => {
          alert(this.myRef1.current.value);
        };
        // 展示右侧输入框的数据
        showData2 = (event) => {
          alert(event.target.value);
        };
        render() {
          return (
            <div>
              <input
                ref={this.myRef1}
                type="text"
                placeholder="点击按钮提示数据"
              ></input>
              <button onClick={this.showData}>点我提示左侧的数据</button>
              <input
                onBlur={this.showData2}
                type="text"
                placeholder="失去焦点提示数据"
              ></input>
            </div>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

5-React 中收集表单数据

5.1-非受控组件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        handleSubmit = (e) => {
          const { username, password } = this;
          e.preventDefault(); // 可以阻止表单的默认跳转
          console.log(username.value, password.value);
        };
        render() {
          return (
            <form onSubmit={this.handleSubmit}>
              用户名:{" "}
              <input
                ref={(c) => (this.username = c)}
                type="text"
                name="username"
              />
              密码: <input
                ref={(c) => (this.password = c)}
                type="password"
                password="password"
              />
              <button>登录</button>
            </form>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

5.2-受控组件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 手控组件的好处就是省掉了过多的ref
       */
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        state = {
          username: "",
          password: "",
        };
        handleSubmit = (e) => {
          e.preventDefault(); // 可以阻止表单的默认跳转
          const { username, password } = this.state;
          console.log(username, password);
        };
        // 保存用户名到state
        changeUsername = (e) => {
          this.setState({ username: e.target.value });
        };
        // 保存密码到state
        changePassword = (e) => {
          this.setState({ password: e.target.value });
        };
        render() {
          return (
            <form onSubmit={this.handleSubmit}>
              用户名:{" "}
              <input
                onChange={this.changeUsername}
                type="text"
                name="username"
              />
              密码: <input
                onChange={this.changePassword}
                type="password"
                password="password"
              />
              <button>登录</button>
            </form>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

6-高阶函数与函数柯里化

6.1-高阶函数和函数柯里化

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 高阶函数: 如果一个函数符合下面两个规范中的任何一个,那么该函数就是高阶函数
       *      1. 若A函数,接收的参数是一个函数,那么A函数就称为高阶函数
       *      2. 若A函数,调用的返回值依然是一个函数,那么A函数就称为高阶函数
       * 常见的高阶函数
       *      1. Promise    new Promise(()=>{})
       *      2. setTimeout
       *      3. arr.map 等等
       *
       *
       * 函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接收参数最后统一处理的函数编码方式
       */
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        state = {
          username: "",
          password: "",
        };
        // 提交表单
        handleSubmit = (event) => {
          event.preventDefault(); // 可以阻止表单的默认跳转
          const { username, password } = this.state;
          console.log(username, password);
        };
        // 保存表单项的数据到状态中
        saveFormData = (dataType) => {
          return (event) => {
            this.setState({ [dataType]: event.target.value });
          };
        };
        render() {
          return (
            <form onSubmit={this.handleSubmit}>
              用户名:{" "}
              <input
                onChange={this.saveFormData("username")}
                type="text"
                name="username"
              />
              密码: <input
                onChange={this.saveFormData("password")}
                type="password"
                password="password"
              />
              <button>登录</button>
            </form>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

6.2-不用高阶函数和函数柯里化

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        state = {
          username: "",
          password: "",
        };
        // 提交表单
        handleSubmit = (event) => {
          event.preventDefault(); // 可以阻止表单的默认跳转
          const { username, password } = this.state;
          console.log(username, password);
        };
        // 保存表单项的数据到状态中
        saveFormData = (dataType, event) => {
          this.setState({ [dataType]: event.target.value });
        };
        render() {
          return (
            <form onSubmit={this.handleSubmit}>
              用户名:{" "}
              <input
                onChange={(event) => {
                  this.saveFormData("username", event);
                }}
                type="text"
                name="username"
              />
              密码: <input
                onChange={(event) => {
                  this.saveFormData("password", event);
                }}
                type="password"
                password="password"
              />
              <button>登录</button>
            </form>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-组件的生命周期

旧生命周期

旧生命周期

新生命周期

7-1.引出生命周期

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test"></div>
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 生命周期回调函数 === 生命周期钩子函数 === 生周期函数 === 生命周期钩子
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        state = { opacity: 1 };
        death = () => {
          // 卸载组件
          ReactDOM.unmountComponentAtNode(document.getElementById("test1"));
        };
        // 组件挂在完毕时候调用
        componentDidMount() {
          this.timer = setInterval(() => {
            let { opacity } = this.state;
            opacity -= 0.1;
            if (opacity <= 0) {
              opacity = 1;
            }
            this.setState({ opacity });
          }, 200);
        }
        // 组件将要卸载时候调用
        componentWillUnmount() {
          clearInterval(this.timer);
        }
        // 初始化渲染, 状态更新之后
        render() {
          return (
            <div>
              <h2 style={{ opacity: this.state.opacity }}>
                React学不会怎么办?
              </h2>
              <button onClick={this.death}>不活了!</button>
            </div>
          );
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-2.生命周期-挂载(旧)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 生命周期回调函数 === 生命周期钩子函数 === 生周期函数 === 生命周期钩子
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
          console.log("挂载 第一步 ---> constructor");
        }
        death = () => {
          ReactDOM.unmountComponentAtNode(document.getElementById("test1"));
        };
        // 组件将要挂载时候调用 (第二步)
        componentWillMount() {
          console.log("挂载 第二步 ---> componentWillMount");
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("挂载 第三步 ---> render");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.death}>卸载组件</button>
            </div>
          );
        }
        // 组件挂载完毕时候调用
        componentDidMount() {
          console.log("挂载 第四步 ---> componentDidMount");
        }
        // 组件将要卸载时候调用
        componentWillUnmount() {
          console.log("挂载 第五步 ---> componentWillUnmount");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-3.生命周期-setState 更新(旧)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 生命周期回调函数 === 生命周期钩子函数 === 生周期函数 === 生命周期钩子
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
        }
        add = () => {
          const { count } = this.state;
          this.setState({
            count: count + 1,
          });
          console.log("更新 第一步 ---> setState");
        };
        // 控制组件更新的开关
        shouldComponentUpdate() {
          console.log("更新 第二步 ---> shouldComponentUpdate");
          return true;
        }
        componentWillUpdate() {
          console.log("更新 第三步 ---> componentWillUpdate");
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("更新 第四步 ---> render");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.add}>点我+1</button>
            </div>
          );
        }
        componentDidUpdate() {
          console.log("更新 第五步 ---> componentDidUpdate");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-4.生命周期-forceUpdate 更新(旧)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 生命周期回调函数 === 生命周期钩子函数 === 生周期函数 === 生命周期钩子
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
        }
        // 强制更新按钮(不会走到shouldComponentUpdate)
        force = () => {
          this.forceUpdate();
          console.log("更新 第一步 ---> forceUpdate");
        };
        componentWillUpdate() {
          console.log("更新 第二步 ---> componentWillUpdate");
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("更新 第三步 ---> render");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.force}>不更新数据强制更新页面</button>
            </div>
          );
        }
        componentDidUpdate() {
          console.log("更新 第四步 ---> componentDidUpdate");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-5.生命周期-父组件 render 更新(旧)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 生命周期回调函数 === 生命周期钩子函数 === 生周期函数 === 生命周期钩子
      // 父组件A
      class A extends React.Component {
        state = { carName: "奔驰" };
        changeCar = () => {
          this.setState({ carName: "奥迪" });
        };
        render() {
          return (
            <div>
              <h1>A</h1>
              <button onClick={this.changeCar}>换车</button>
              <B carName={this.state.carName} />
            </div>
          );
        }
      }
      // 子组件B
      class B extends React.Component {
        // 这个钩子第一次传的不算
        componentWillReceiveProps(props) {
          console.log("子组件更新 第一步 ---> componentWillReceiveProps");
          console.log(props);
        }
        // 控制组件更新的开关
        shouldComponentUpdate() {
          console.log("子组件更新 第二步 ---> shouldComponentUpdate");
          return true;
        }
        componentWillUpdate() {
          console.log("子组件更新 第三步 ---> componentWillUpdate");
        }
        render() {
          console.log("子组件更新 第四步 ---> render");
          return <div>B -- {this.props.carName}</div>;
        }
        componentDidUpdate() {
          console.log("子组件更新 第五步 ---> componentDidUpdate");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<A />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-6.生命周期总结(旧)

/**
 * 常用:
 *      1. componentDidMount() 一般做一些初始化的事儿,例如开始定制器或者发送网络请求或者订阅消息
 *      2. componentWillUnmount() 一般做一些收尾的事儿,例如取消定时器
 *
 * 1. 初始化阶段: 由 ReactDOM.render() 触发---初次渲染
 *      1. constructor()
 *      2. componentWillMount()
 *      3. render()
 *      4. componentDidMount()
 * 2. 更新阶段: 由组件内部 setState() 或者父组件 render() 触发
 *      1. shouldComponentUpdate()
 *      2. componentWillUpdate()
 *      3. render()
 *      4. componentDidMount()
 * 3. 更新阶段: 由组件内部 forceUpdate() 触发
 *      1. componentWillUpdate()
 *      2. render()
 *      3. componentDidMount()
 * 4. 父组件Props更新: 由父组件Props更新导致子组件更新
 *      1. componentWillReceiveProps()
 *      2. shouldComponentUpdate()
 *      3. componentWillUpdate()
 *      4. render()
 *      5. componentDidMount()
 * 5. 卸载组件: 由 ReactDOM.unmountComponentAtNode() 触发
 *      1. componentWillUnmount()
 */
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 生命周期回调函数 === 生命周期钩子函数 === 生周期函数 === 生命周期钩子
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
          console.log("挂载 第一步 ---> constructor");
        }
        death = () => {
          ReactDOM.unmountComponentAtNode(document.getElementById("test1"));
        };
        // 组件将要挂载时候调用 (第二步)
        componentWillMount() {
          console.log("挂载 第二步 ---> componentWillMount");
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("挂载 第三步 ---> render");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.death}>卸载组件</button>
            </div>
          );
        }
        // 组件挂载完毕时候调用
        componentDidMount() {
          console.log("挂载 第四步 ---> componentDidMount");
        }
        // 组件将要卸载时候调用
        componentWillUnmount() {
          console.log("挂载 第五步 ---> componentWillUnmount");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-7.生命周期-挂载(新)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/17/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/17/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 所有带 will 的钩子函数都需要加上 UNSAFE_
      // 1. 创建函数式组件
      /***
       * 更新了三个钩子函数
       * 1. componentWillMount UNSAFE_componentWillMount
       * 2. componentWillUpdate UNSAFE_componentWillUpdate
       * 3. componentWillReceiveProps componentWillReceiveProps
       */
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
          console.log("挂载 第一步 ---> constructor");
        }
        death = () => {
          ReactDOM.unmountComponentAtNode(document.getElementById("test1"));
        };
        // 组件将要挂载时候调用 - 旧 (第二步)
        componentWillMount() {
          console.log("挂载 第二步 ---> componentWillMount");
        }
        // 组件将要挂载时候调用 - 新 (第二步)
        UNSAFE_componentWillMount() {
          console.log("挂载 第二步 ---> UNSAFE_componentWillMount");
        }
        // 组件将要更新数据时调用 - 旧
        componentWillUpdate() {
          console.log("子组件更新 第三步 ---> componentWillUpdate");
        }
        // 组件将要更新数据时调用 - 新
        UNSAFE_componentWillUpdate() {
          console.log("子组件更新 第三步 ---> UNSAFE_componentWillUpdate");
        }
        // 当props发生变化时调用 - 旧
        // 这个钩子第一次传的不算
        componentWillReceiveProps(props) {
          console.log("子组件更新 第一步 ---> componentWillReceiveProps");
          console.log(props);
        }
        // 当props发生变化时调用 - 新
        // 这个钩子第一次传的不算
        UNSAFE_componentWillReceiveProps(props) {
          console.log(
            "子组件更新 第一步 ---> UNSAFE_componentWillReceiveProps"
          );
          console.log(props);
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("挂载 第三步 ---> render");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.death}>卸载组件</button>
            </div>
          );
        }
        // 组件挂载完毕时候调用
        componentDidMount() {
          console.log("挂载 第四步 ---> componentDidMount");
        }
        // 组件将要卸载时候调用
        componentWillUnmount() {
          console.log("挂载 第五步 ---> componentWillUnmount");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(<MyComponent />, document.getElementById("test1"));
    </script>
  </body>
</html>

7-8.生命周期-getDerivedStateFromProps(新)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/17/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/17/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 所有带 will 的钩子函数都需要加上 UNSAFE_
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
          console.log("挂载 第一步 ---> constructor");
        }
        death = () => {
          ReactDOM.unmountComponentAtNode(document.getElementById("test1"));
        };
        // 如果State的任何值都取决于Props,那么就可以使用这个,但是非必须使用
        // 会在调用 render 方法之前调用,
        // 即在渲染 DOM 元素之前会调用,并且在初始挂载及后续更新时都会被调用.
        static getDerivedStateFromProps(props, state) {
          console.log("props", props); // {count: 199}
          console.log("state", state); // {count: 0}
          console.log("挂载 第二步 ---> getDerivedStateFromProps");
          // 每次更新也会走到这里, 那么每次都会return这个数据, 则页面的数据一直不会改变
          // 都会展示 Props 的值
          return props; // 返回 State 则会影响 State
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("挂载 第三步 ---> render");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.death}>卸载组件</button>
            </div>
          );
        }
        // 组件挂载完毕时候调用
        componentDidMount() {
          console.log("挂载 第四步 ---> componentDidMount");
        }
        // 组件将要卸载时候调用
        componentWillUnmount() {
          console.log("挂载 第五步 ---> componentWillUnmount");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(
        <MyComponent count={199} />,
        document.getElementById("test1")
      );
    </script>
  </body>
</html>

7-9.生命周期-getSnapshotBeforeUpdate(新)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/17/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/17/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 所有带 will 的钩子函数都需要加上 UNSAFE_
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
          console.log("挂载 第一步 ---> constructor");
        }
        death = () => {
          ReactDOM.unmountComponentAtNode(document.getElementById("test1"));
        };
        update = () => {
          this.setState({ count: 1 });
        };
        /**
         * getSnapshotBeforeUpdate() 方法在最近一次渲染输出(提交到 DOM 节点)之前调用.
         * 在 getSnapshotBeforeUpdate() 方法中,我们可以访问更新前的 props 和 state.
         * getSnapshotBeforeUpdate() 方法需要与 componentDidUpdate() 方法一起使用,否则会出现错误.
         *
         * 组件更新完成前做一些操作
         */
        getSnapshotBeforeUpdate() {
          console.log("更新 第二步 ---> getSnapshotBeforeUpdate");
          return "getSnapshotBeforeUpdate";
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("挂载 第三步 ---> render");
          console.log("更新 第一步 ---> getSnapshotBeforeUpdate");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.death}>卸载组件</button>
              <button onClick={this.update}>更新数据</button>
            </div>
          );
        }
        // 组件挂载完毕时候调用
        componentDidUpdate(preProps, preState, snapValue) {
          // 这俩哥参数都是修改之前的
          console.log(preProps, preState); // {count: 199} {count: 0}

          // 这个是 上个周期 getSnapshotBeforeUpdate 传过来的 snapValue
          console.log("getSnapshotBeforeUpdate传过来的数据--->", snapValue); // getSnapshotBeforeUpdate
          console.log("更新 第三步 ---> componentDidUpdate");
        }
        // 组件挂载完毕时候调用
        componentDidMount() {
          console.log("挂载 第四步 ---> componentDidMount");
        }
        // 组件将要卸载时候调用
        componentWillUnmount() {
          console.log("挂载 第五步 ---> componentWillUnmount");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(
        <MyComponent count={199} />,
        document.getElementById("test1")
      );
    </script>
  </body>
</html>

7-10.getSnapshotBeforeUpdate 使用场景

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
    <style>
      .list {
        width: 200px;
        height: 150px;
        background-color: yellow;
        overflow: auto;
      }

      .news {
        height: 30px;
      }
    </style>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/17/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/17/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      // 所有带 will 的钩子函数都需要加上 UNSAFE_
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { newsArr: [] };
        }
        componentDidMount() {
          setInterval(() => {
            const { newsArr } = this.state;
            const news = "新闻" + (newsArr.length + 1);
            this.setState({ newsArr: [news, ...newsArr] });
          }, 1000);
        }
        getSnapshotBeforeUpdate() {
          return this.refs.list.scrollHeight;
        }
        // 初始化渲染, 状态更新之后
        render() {
          return (
            <div className="list" ref="list">
              {this.state.newsArr.map((el) => {
                return (
                  <div key={el} className="news">
                    {el}
                  </div>
                );
              })}
            </div>
          );
        }
        // 组件挂载完毕时候调用
        componentDidUpdate(preProps, preState, height) {
          this.refs.list.scrollTop += this.refs.list.scrollHeight - height;
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(
        <MyComponent count={199} />,
        document.getElementById("test1")
      );
    </script>
  </body>
</html>

7-11.生命周期总结(新)

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>01-函数式组件</title>
  </head>

  <body>
    <!-- 准备好容器 -->
    <div id="test1"></div>
    <!-- 引入React核心库. -->
    <script src="../React-js/17/react.development.js"></script>
    <!-- 引入React-dom,用于支持操作DOM -->
    <script src="../React-js/17/react-dom.development.js"></script>
    <!-- 引入babel,用于将jsx转为js -->
    <script src="../React-js/babel.min.js"></script>
    <!-- 写这个type的意义是为了让他知道我写的是jsx -->
    <script type="text/babel">
      /**
       * 常用:
       *      1. componentDidMount() 一般做一些初始化的事儿,例如开始定制器或者发送网络请求或者订阅消息
       *      2. componentWillUnmount() 一般做一些收尾的事儿,例如取消定时器
       *
       * 1. 初始化阶段: 由 ReactDOM.render() 触发---初次渲染
       *      1. constructor()
       *      2. getDerivedStateFromProps()
       *      3. render()
       *      4. componentDidMount()
       * 2. 更新阶段: 由组件内部 setState() 或者父组件 render() 触发
       *      1. getDerivedStateFromProps()
       *      2. shouldComponentUpdate()
       *      3. render()
       *      4. getSnapshotBeforeUpdate()
       *      4. componentWillUpdate()
       * 3. 卸载组件: 由 ReactDOM.unmountComponentAtNode() 触发
       *      1. componentWillUnmount()
       */
      // 所有带 will 的钩子函数都需要加上 UNSAFE_
      // 1. 创建函数式组件
      class MyComponent extends React.Component {
        // 构造器 (第一步)
        constructor(props) {
          super(props);
          // 初始化状态
          this.state = { count: 0 };
          console.log("挂载 第一步 ---> constructor");
        }
        death = () => {
          ReactDOM.unmountComponentAtNode(document.getElementById("test1"));
        };
        update = () => {
          this.setState({ count: 1 });
        };
        /**
         * getSnapshotBeforeUpdate() 方法在最近一次渲染输出(提交到 DOM 节点)之前调用.
         * 在 getSnapshotBeforeUpdate() 方法中,我们可以访问更新前的 props 和 state.
         * getSnapshotBeforeUpdate() 方法需要与 componentDidUpdate() 方法一起使用,否则会出现错误.
         *
         * 组件更新完成前做一些操作
         */
        getSnapshotBeforeUpdate() {
          console.log("更新 第二步 ---> getSnapshotBeforeUpdate");
          return "getSnapshotBeforeUpdate";
        }
        // 初始化渲染, 状态更新之后
        render() {
          console.log("挂载 第三步 ---> render");
          console.log("更新 第一步 ---> getSnapshotBeforeUpdate");
          return (
            <div>
              <h2>当前求和为: {this.state.count}</h2>
              <button onClick={this.death}>卸载组件</button>
              <button onClick={this.update}>更新数据</button>
            </div>
          );
        }
        // 组件挂载完毕时候调用
        componentDidUpdate(preProps, preState, snapValue) {
          // 这俩哥参数都是修改之前的
          console.log(preProps, preState); // {count: 199} {count: 0}

          // 这个是 上个周期 getSnapshotBeforeUpdate 传过来的 snapValue
          console.log("getSnapshotBeforeUpdate传过来的数据--->", snapValue); // getSnapshotBeforeUpdate
          console.log("更新 第三步 ---> componentDidUpdate");
        }
        // 组件挂载完毕时候调用
        componentDidMount() {
          console.log("挂载 第四步 ---> componentDidMount");
        }
        // 组件将要卸载时候调用
        componentWillUnmount() {
          console.log("挂载 第五步 ---> componentWillUnmount");
        }
      }
      // 2.渲染组件到页面
      ReactDOM.render(
        <MyComponent count={199} />,
        document.getElementById("test1")
      );
    </script>
  </body>
</html>

React 脚手架

01-脚手架自带文件

可以避免同名 css 被覆盖的问题,后引入的组件样式会覆盖同名的前面的样式

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";

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

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import Hello from "./components/Hello";
import Welcome from "./components/Welcome";
export default class App extends Component {
  render() {
    return (
      <div>
        <Hello />
        <Welcome />
      </div>
    );
  }
}

Hello.jsx

import React, { Component } from "react";
import hello from "./index.module.css";
export default class Hello extends Component {
  render() {
    return (
      <div className={hello.hello}>
        <h2>Hello</h2>
      </div>
    );
  }
}

Welcome.jsx

import React, { Component } from "react";
import "./index.css";
export default class Welcome extends Component {
  render() {
    return (
      <div className="welcome">
        <h1>Welcome</h1>
      </div>
    );
  }
}

03-配置代理 setupProxy

setupProxy.js

const proxy = require("http-proxy-middleware");

module.exports = function (app) {
  app.use(
    proxy("/api1", {
      // 遇见api1前缀的请求就会触发该代理配置
      targer: "http://localhost:5000", // 请求转发给谁
      changeOrigin: true, // 控制服务器收到的响应头重host字段的值
      /*
      	changeOrigin设置为true时,服务器收到的请求头中的host为:localhost:5000
      	changeOrigin设置为false时,服务器收到的请求头中的host为:localhost:3000
      	changeOrigin默认值为false,但我们一般将changeOrigin值设为true
      */
      pathRewrite: { "^/api1": "" }, //去除请求前缀,保证交给后台服务器的是正常请求地址(必须配置)
    })
  );
  app.use(
    proxy("/api2", {
      targer: "http://localhost:5001",
      changeOrigin: true,
      pathRewrite: { "^/api2": "" },
    })
  );
};

04-消息订阅与发布

Hello.jsx

import React, { Component } from "react";
import PubSub from "pubsub-js";

export default class Hello extends Component {
  talkWecome = () => {
    PubSub.publish("talkWecome", { info: "hello发送数据给welcome" });
  };
  render() {
    return (
      <div>
        <h2>Hello</h2>
        <button onClick={this.talkWecome}>告诉welcome</button>
      </div>
    );
  }
}

Welcome.jsx

import React, { Component } from "react";
import PubSub from "pubsub-js";
export default class Welcome extends Component {
  state = { info: "" };
  componentDidMount() {
    this.token = PubSub.subscribe("talkWecome", (_, data) => {
      this.setState(data);
    });
  }
  componentWillUnmount() {
    PubSub.unsubscribe(this.token);
  }
  render() {
    return (
      <div className="welcome">
        <h1>Welcome</h1>
        <h2>{this.state.info}</h2>
      </div>
    );
  }
}

05-路由的基本使用

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, HashRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";

ReactDOM.render(
  // 一个项目只允许有一个 BrowserRouter
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, BrowserRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        <Link to="/about"> About </Link>
        <Link to="/home"> Home </Link>
        {/* 注册路由 */}
        <Route path="/about" component={About} />
        <Route path="/home" component={Home} />
      </div>
    );
  }
}

About.jsx

import React, { Component } from "react";
export default class About extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px", color: "red" }}>
              About
            </a>
            <a>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>About</h2>
        </div>
      </div>
    );
  }
}

Home.jsx

import React, { Component } from "react";
export default class Home extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px" }}>About</a>
            <a style={{ color: "red" }}>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>Home</h2>
        </div>
      </div>
    );
  }
}

06-NavLink 的使用

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter, HashRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";

ReactDOM.render(
  // 一个项目只允许有一个 BrowserRouter
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, BrowserRouter, Route } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
import Hello from "./components/Hello";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        <NavLink activeClassName="demo" to="/about">
          About
        </NavLink>
        <NavLink activeClassName="demo" to="/home">
          Home
        </NavLink>
        {/* 注册路由 */}
        <Route path="/about" component={About} />
        <Route path="/home" component={Home} />
        {/* <Hello a={123}/> */}
      </div>
    );
  }
}

About.jsx

import React, { Component } from "react";
export default class About extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px", color: "red" }}>
              About
            </a>
            <a>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>About</h2>
        </div>
      </div>
    );
  }
}

Home.jsx

import React, { Component } from "react";
export default class Home extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <h1>React Router Demo</h1>
        <div style={{ border: "1px solid red" }}>
          <h2>导航区</h2>
          <div>
            <a style={{ float: "left", marginRight: "10px" }}>About</a>
            <a style={{ color: "red" }}>Home</a>
          </div>
        </div>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h2>展示区</h2>
          <h2>Home</h2>
        </div>
      </div>
    );
  }
}

07-封装 MyNavLink

03-React-cli\07-封装 MyNavLink\components\MyNavLink\index.jsx

import React, { Component } from "react";
import { NavLink } from "react-router-dom";

export default class MyNavLink extends Component {
  render() {
    console.log(this.props);
    return (
      <div>
        <NavLink {...this.props} activeClassName="demo" to={this.props.to} />
      </div>
    );
  }
}

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, BrowserRouter, Route } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
import Hello from "./components/Hello";
import MyNavLink from "./components/MyNavLink";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        {/* 接收标签体内容 */}
        {/* 标签体内容也是一个特殊的标签属性 在props里面的 children */}
        <MyNavLink to="/about">about</MyNavLink>
        <MyNavLink to="/home">home</MyNavLink>
        {/* 注册路由 */}
        <Route path="/about" component={About} />
        <Route path="/home" component={Home} />
        {/* <Hello a={123}/> */}
      </div>
    );
  }
}

08-Switch 的使用

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, BrowserRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Test from "./components/Test";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        <Link to="/about"> About </Link>
        <Link to="/home"> Home </Link>
        {/* 注册路由 */}
        {/* 这个组件只会让路由注册一个,匹配到后下面的组件就不展示了 */}
        <Switch>
          <Route path="/about" component={About} />
          <Route path="/home" component={Home} />
          <Route path="/home" component={Test} />
        </Switch>
      </div>
    );
  }
}

09-解决样式丢失问题

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, HashRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Test from "./components/Test";
import MyNavLink from "./components/MyNavLink";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 这样写法会出现样式问题,因为 /LGQ/ 会被浏览器当做路径去处理 */}
        {/* 解决方案 */}
        {/* 1. <link href="/css/bootstrap.css" rel="stylesheet">  */}
        {/* 2. <link href="%PUBLIC_URL%/css/bootstrap.css" rel="stylesheet">  */}
        {/* 3. 修改 BrowserRouter 为 HashRouter */}
        <MyNavLink to="/LGQ/about">About</MyNavLink>
        <MyNavLink to="/LGQ/home">Home</MyNavLink>
        {/* 注册路由 */}
        <Switch>
          <Route path="/LGQ/about" component={About} />
          <Route path="/LGQ/home" component={Home} />
          <Route path="/LGQ/home" component={Test} />
        </Switch>
        {/* <Hello a={123}/> */}
      </div>
    );
  }
}

10-模糊匹配与精准匹配

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Link, NavLink, HashRouter, Route } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Test from "./components/Test";
import MyNavLink from "./components/MyNavLink";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 模糊匹配路由: Route 要的 MyNavLink 必须一个不能少,但是可以多 */}
        {/* to="/home/a/b" path="/home" 可以 */}
        {/* to="/home/" path="/homea/b" 不可以 */}

        {/* 严格匹配路由: exact */}
        <MyNavLink to="/about">About</MyNavLink>
        <MyNavLink to="/home/a/b">Home</MyNavLink>
        {/* 注册路由 */}
        <Switch>
          <Route exact path="/about" component={About} />
          <Route exact path="/home" component={Home} />
          <Route exact path="/test" component={Test} />
        </Switch>
      </div>
    );
  }
}

11-嵌套路由的使用

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { Route, Redirect } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
import MyNavLink from "./components/MyNavLink";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";
export default class App extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        <MyNavLink to="/about">About</MyNavLink>
        <MyNavLink to="/home">Home</MyNavLink>
        {/* 注册路由 */}
        <Switch>
          <Route path="/about" component={About} />
          <Route path="/home" component={Home} />
          <Redirect to="/about"></Redirect>
        </Switch>
      </div>
    );
  }
}

Home.jsx

import React, { Component } from "react";
import MyNavLink from "../../components/MyNavLink";
import { Route } from "react-router-dom";
import { Switch } from "react-router-dom/cjs/react-router-dom.min";

import Message from "./Message";
import News from "./News";
export default class Home extends Component {
  render() {
    return (
      <div style={{ padding: "10px" }}>
        <MyNavLink to="/home/message">Message</MyNavLink>
        <MyNavLink to="/home/news">News</MyNavLink>
        <div style={{ border: "1px solid green", marginTop: "10px" }}>
          <h4>展示区</h4>
          <h4>Home</h4>
          <Switch>
            <Route path="/home/message" component={Message} />
            <Route path="/home/news" component={News} />
          </Switch>
        </div>
      </div>
    );
  }
}

Message.jsx

import React, { Component } from "react";

export default class Message extends Component {
  render() {
    return (
      <div>
        <ul>
          <li>Message 1</li>
          <li>Message 2</li>
          <li>Message 3</li>
          <li>Message 4</li>
        </ul>
      </div>
    );
  }
}

News.jsx

import React, { Component } from "react";

export default class News extends Component {
  render() {
    return (
      <div>
        <ul>
          <li>News 1</li>
          <li>News 2</li>
          <li>News 3</li>
          <li>News 3</li>
        </ul>
      </div>
    );
  }
}

12-路由组件传递 params 参数

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link>
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        <Route path="/home/message/detail/:id/:title" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 Params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    return (
      <div>
        <p>ID: {this.props.match.params.id}</p>
        <p>Title: {this.props.match.params.title}</p>
        <p>Content: {data[this.props.match.params.id]}</p>
      </div>
    );
  }
}

13-路由组件传递 search 参数

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              {/* <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link> */}

              {/* 2. Link 携带 search 参数 */}
              <Link to={`/home/message/detail/?id=${el.id}&title=${el.title}`}>
                {el.title}
              </Link>
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}

        {/* 2. search 参数无需声明接收 */}
        <Route path="/home/message/detail" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
import qs from "querystring";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    // return (
    //   <div>
    //     <p>ID: {this.props.match.params.id}</p>
    //     <p>Title: {this.props.match.params.title}</p>
    //     <p>Content: {data[this.props.match.params.id]}</p>
    //   </div>
    // );

    // 2. 接受 search 参数
    // key=value&key=value  这种形式是 urlencoded编码
    const { id, title } = qs.parse(this.props.location.search.slice(1));
    return (
      <div>
        <p>ID: {id}</p>
        <p>Title: {title}</p>
        <p>Content: {data[id]}</p>
      </div>
    );
  }
}

14-路由组件传递 state 参数

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              {/* <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link> */}

              {/* 2. Link 携带 search 参数 */}
              {/* <Link to={`/home/message/detail/?id=${el.id}&title=${el.title}`}>
                {el.title}
              </Link> */}

              {/* 3. Link 携带 state 参数 */}
              <Link
                to={{
                  pathname: "/home/message/detail",
                  state: { id: el.id, title: el.title },
                }}
              >
                {el.title}
              </Link>
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}

        {/* 2. search 参数无需声明接收 */}
        {/* <Route path="/home/message/detail" component={Detail} /> */}

        {/* 3. state 参数无需声明接收 */}
        <Route path="/home/message/detail" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
import qs from "querystring";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    // return (
    //   <div>
    //     <p>ID: {this.props.match.params.id}</p>
    //     <p>Title: {this.props.match.params.title}</p>
    //     <p>Content: {data[this.props.match.params.id]}</p>
    //   </div>
    // );

    // 2. 接受 search 参数
    // key=value&key=value  这种形式是 urlencoded编码
    // const { id, title } = qs.parse(this.props.location.search.slice(1));
    // return (
    //   <div>
    //     <p>ID: {id}</p>
    //     <p>Title: {title}</p>
    //     <p>Content: {data[id]}</p>
    //   </div>
    // );

    // 3. 接受 state 参数
    const { id, title } = this.props.location.state;
    return (
      <div>
        <p>ID: {id}</p>
        <p>Title: {title}</p>
        <p>Content: {data[id]}</p>
      </div>
    );
  }
}

15-路由的 replace 模式

messages.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 3. Link 携带 state 参数 */}
              {/* replace模式就不会留下记录 */}
              <Link
                replace
                to={{
                  pathname: "/home/message/detail",
                  state: { id: el.id, title: el.title },
                }}
              >
                {el.title}
              </Link>
            </li>
          );
        })}
      </div>
    );
  }
}

16-编程式路由导航

Message.jsx

import React, { Component } from "react";
import Detail from "./Detail";
import { Link, Route } from "react-router-dom/cjs/react-router-dom.min";
export default class Message extends Component {
  state = {
    messages: [
      { id: 1, title: "message1" },
      { id: 2, title: "message2" },
      { id: 3, title: "message3" },
      { id: 4, title: "message4" },
    ],
  };
  pushShow = (el) => {
    // push 跳转携带 params 参数
    // this.props.history.push(`/home/message/detail/${el.id}/${el.title}`);

    // push 跳转携带 search 参数
    // this.props.history.push(
    //   `/home/message/detail/?id=${el.id}&title=${el.title}`
    // );

    // push 跳转携带 state 参数
    this.props.history.push("/home/message/detail", {
      id: el.id,
      title: el.title,
    });
  };
  replaceShow = (el) => {
    // replace 跳转携带params参数
    // this.props.history.replace(`/home/message/detail/${el.id}/${el.title}`);

    // replace 跳转携带 search 参数
    // this.props.history.replace(
    //   `/home/message/detail/?id=${el.id}&title=${el.title}`
    // );

    // replace 跳转携带 state 参数
    this.props.history.replace("/home/message/detail", {
      id: el.id,
      title: el.title,
    });
  };
  render() {
    return (
      <div>
        {this.state.messages.map((el) => {
          return (
            <li key={el.id}>
              {/* 1. Link 携带 params 参数 */}
              <Link to={`/home/message/detail/${el.id}/${el.title}`}>
                {el.title}
              </Link>
              <button onClick={() => this.pushShow(el)}>push查看</button>
              <button onClick={() => this.replaceShow(el)}>replace查看</button>
              {/* 2. Link 携带 search 参数 */}
              {/* <Link to={`/home/message/detail/?id=${el.id}&title=${el.title}`}>
                {el.title}
              </Link> */}

              {/* 3. Link 携带 state 参数 */}
              {/* <Link
                to={{
                  pathname: "/home/message/detail",
                  state: { id: el.id, title: el.title },
                }}
              >
                {el.title}
              </Link> */}
            </li>
          );
        })}
        {/* 1. Route 声明接收 params 参数 */}
        {/* <Route path="/home/message/detail/:id/:title" component={Detail} /> */}

        {/* 2. search 参数无需声明接收 */}
        {/* <Route path="/home/message/detail" component={Detail} /> */}

        {/* 3. state 参数无需声明接收 */}
        <Route path="/home/message/detail" component={Detail} />
      </div>
    );
  }
}

Detail.jsx

import React, { Component } from "react";
import qs from "querystring";
const data = {
  1: "你好,中国",
  2: "你好,上海",
  3: "你好,杭州",
  4: "你好,郑州",
};
export default class Detail extends Component {
  render() {
    console.log(this.props);
    // 1. 接受 params 参数
    // {
    //     "match": {
    //         "path": "/home/message/detail/:id/:title",
    //         "url": "/home/message/detail/3/Message3",
    //         "isExact": true,
    //         "params": {
    //             "id": "3",
    //             "title": "Message3"
    //         }
    //     }
    // }
    // return (
    //   <div>
    //     <p>ID: {this.props.match.params.id}</p>
    //     <p>Title: {this.props.match.params.title}</p>
    //     <p>Content: {data[this.props.match.params.id]}</p>
    //   </div>
    // );

    // 2. 接受 search 参数
    // key=value&key=value  这种形式是 urlencoded编码
    // const { id, title } = qs.parse(this.props.location.search.slice(1));
    // return (
    //   <div>
    //     <p>ID: {id}</p>
    //     <p>Title: {title}</p>
    //     <p>Content: {data[id]}</p>
    //   </div>
    // );

    // 3. 接受 state 参数
    const { id, title } = this.props.location.state;
    return (
      <div>
        <p>ID: {id}</p>
        <p>Title: {title}</p>
        <p>Content: {data[id]}</p>
      </div>
    );
  }
}

17-withRouter 的使用

Header.jsx

import React, { Component } from "react";
import { withRouter } from "react-router-dom/cjs/react-router-dom.min";
class Header extends Component {
  back = () => {
    this.props.history.goBack();
  };
  forward = () => {
    this.props.history.goForward();
  };
  go = () => {
    this.props.history.back();
  };
  render() {
    console.log(this.props);
    return (
      <div>
        <h1>我是APP</h1>
        <button onClick={this.back}>回退</button>
        <button onClick={this.forward}>前进</button>
        <button onClick={this.go}>go</button>
      </div>
    );
  }
}
// withRouter 可以把一般组件加工成路由组件,让一般组件的props有路由组件的API
// withRouter的返回值是一个新组件

export default withRouter(Header);

18-求和 React 版本

Count.jsx

import React, { Component } from "react";

export default class Count extends Component {
  state = { count: 0 };
  increment = () => {
    const { value } = this.selectNumber;
    const count = this.state.count + Number(value);
    this.setState({
      count,
    });
  };
  decrement = () => {
    const { value } = this.selectNumber;
    const count = this.state.count - Number(value);
    this.setState({
      count,
    });
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    const count = this.state.count + Number(value);
    if (this.state.count % 2 !== 0) {
      this.setState({
        count,
      });
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    const count = this.state.count + Number(value);
    setTimeout(() => {
      this.setState({
        count,
      });
    }, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {this.state.count}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

19-Redux 精简版

Count.jsx

import React, { Component } from "react";
// 引入 store
import store from "../../redux/store";
console.log(store);
export default class Count extends Component {
  // componentDidMount() {
  //   // 检测redux中数据的变化,变化就调用render
  //   store.subscribe(() => {
  //     this.setState({});
  //   });
  // }
  increment = () => {
    const { value } = this.selectNumber;
    store.dispatch({ type: "increment", data: value - 0 });
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch({ type: "decrement", data: value - 0 });
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (store.getState() % 2 !== 0) {
      store.dispatch({ type: "increment", data: value - 0 });
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    setTimeout(() => {
      store.dispatch({ type: "increment", data: value - 0 });
    }, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

03-React-cli\19-Redux 精简版\redux\count_reducer.js

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  //   console.log(type, data);
  switch (type) {
    case "increment":
      return preState + data;
    case "decrement":
      return preState - data;
    default:
      return preState;
  }
}

03-React-cli\19-Redux 精简版\redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore } from "redux";
// 引入为 count 组件服务的 reducer
import countReducer from "./count_reducer";

export default createStore(countReducer);

20-Redux 完整版

Count.jsx

import React, { Component } from "react";
// 引入 store
import store from "../../redux/store";
import {
  createIncrementAction,
  createDecrementAction,
} from "../../redux/count_action";
console.log(store);
export default class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAction(value - 0));
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAction(value - 0));
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (store.getState() % 2 !== 0) {
      store.dispatch(createIncrementAction(value - 0));
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    setTimeout(() => {
      store.dispatch(createIncrementAction(value - 0));
    }, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

redux\constant.js

// 该模块用于定义action对象中type类型的值
export const INCREMENT = "increment";
export const DECREMENT = "decrement";

redux\count_action.js

import { INCREMENT, DECREMENT } from "./constant";

// 该文件专门为count组件生成action对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });

redux\count_reducer.js

import { INCREMENT, DECREMENT } from "./constant";

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  //   console.log(type, data);
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      return preState;
  }
}

redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore } from "redux";
// 引入为 count 组件服务的 reducer
import countReducer from "./count_reducer";

export default createStore(countReducer);

21-Redux 异步

Count.jsx

import React, { Component } from "react";
// 引入 store
import store from "../../redux/store";
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";
export default class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAction(value - 0));
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch(createDecrementAction(value - 0));
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (store.getState() % 2 !== 0) {
      store.dispatch(createIncrementAction(value - 0));
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    store.dispatch(createIncrementAsyncAction(value - 0, 500));
  };
  render() {
    return (
      <div>
        <h1>当前求和为: {store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入为 count 组件服务的 reducer
import countReducer from "./count_reducer";

export default createStore(countReducer, applyMiddleware(thunk));

redux\count_action.js

import { INCREMENT, DECREMENT } from "./constant";
// 该文件专门为count组件生成action对象

// 同步 action 也就是 action 值为对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
// 异步 action 也就是 action 值为函数,异步 action 不是必须要用的
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, time);
  };
};

22-React-Redux 的基本使用

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下
store.subscribe(() => {
  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById("root")
  );
});
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import CountUI from "./containers/Count";
import store from "./redux/store";
export default class App extends Component {
  render() {
    return (
      <div>
        <CountUI store={store} />
      </div>
    );
  }
}

03-React-cli\22-React-Redux 的基本使用\containers\Count\index.jsx

// 引入CountUI组件
import CountUI from "../../components/Count";

import { INCREMENT, DECREMENT } from "../../redux/constant";
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

// 引入 store ,这里的 store 不能自己去引入,要去上层使用 props 传过来

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

/**
 * 1. 因为 props 取的时候得 key value 的形式,所以需要对象的形式,
 * 2. 返回的对象作为对象会传给组件 - 状态
 * 3. a 函数的返回值作为状态返回给了UI组件
 */
function mapStateToProps(state) {
  // state 是他们传给我们,不需要自己引入
  return { count: state };
}
/**
 * 1. 因为 props 取的时候得 key value 的形式,所以需要对象的形式,
 * 2. 返回的对象作为对象会传给组件 - 操作状态的方法
 */
function mapDispatchToProps(dispatch) {
  // dispatch 也是他们传给我们的,不需要我们自己引入
  return {
    increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
    decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
    incrementAsync: (data, time) =>
      dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  };
}
// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);

03-React-cli\22-React-Redux 的基本使用\components\Count

import React, { Component } from "react";
export default class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和值为: {this.props.count}</h1>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下
store.subscribe(() => {
  ReactDOM.render(
    <BrowserRouter>
      <App />
    </BrowserRouter>,
    document.getElementById("root")
  );
});
ReactDOM.render(
  <BrowserRouter>
    <App />
  </BrowserRouter>,
  document.getElementById("root")
);

23-react-redux 优化

03-React-cli\23-react-redux 优化\containers\Count

// 定义CountUI组件
import React, { Component } from "react";

import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和值为: {this.props.count}</h1>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(
  (state) => ({ count: state }),

  // mapStateToProps 一般写法
  // (dispatch) => ({
  //   increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
  //   decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
  //   incrementAsync: (data, time) =>
  //     dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  // })

  // mapStateToProps 简易写法
  {
    increment: createIncrementAction,
    decrement: createDecrementAction,
    incrementAsync: createIncrementAsyncAction,
  }
)(Count);

App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import Count from "./containers/Count";
export default class App extends Component {
  render() {
    return (
      <div>
        <Count />
      </div>
    );
  }
}

index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
import { Provider } from "react-redux";

// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下

// 但是,用上 react-redux 之后就不需要这个了
// store.subscribe(() => {
//   ReactDOM.render(
//     <BrowserRouter>
//       <App />
//     </BrowserRouter>,
//     document.getElementById("root")
//   );
// });

ReactDOM.render(
  // 在这里这样写的话就不需要每个组件都传 store 了
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

24-react-redux 组件间数据共享

03-React-cli\24-react-redux 组件间数据共享\containers\Count\index.jsx

// 定义CountUI组件
import React, { Component } from "react";

import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/actions/count";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h4>我是 Count 组件,下方组件人数为{this.props.person.length}</h4>
        <h5>当前求和值为: {this.props.count}</h5>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(
  (state) => ({ person: state.person, count: state.count }),

  // mapStateToProps 一般写法
  // (dispatch) => ({
  //   increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
  //   decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
  //   incrementAsync: (data, time) =>
  //     dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  // })

  // mapStateToProps 简易写法
  {
    increment: createIncrementAction,
    decrement: createDecrementAction,
    incrementAsync: createIncrementAsyncAction,
  }
)(Count);

03-React-cli\24-react-redux 组件间数据共享\containers\Person\index.jsx

import React, { Component } from "react";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

import { createAddPersonAction } from "../../redux/actions/person";
class Person extends Component {
  AddPerson = () => {
    const name = this.nameNode.value;
    const age = this.ageNode.value;
    this.props.AddPerson({ name, age });
    this.nameNode.value = this.ageNode.value = "";
  };
  render() {
    return (
      <div>
        <h4>我是 Person 组件,上方组件求和为{this.props.count}</h4>
        <input
          ref={(c) => (this.nameNode = c)}
          type="text"
          placeholder="输入名字"
        />
        <input
          ref={(c) => (this.ageNode = c)}
          type="text"
          placeholder="输入年龄"
        />
        <button onClick={this.AddPerson}>添加</button>
        <ul>
          {this.props.person.map((el) => {
            return (
              <li key={el.id}>
                名字{el.name},年龄{el.age}
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default connect(
  (state) => ({ person: state.person, count: state.count }),
  {
    AddPerson: createAddPersonAction,
  }
)(Person);

redux

actions
// 03-React-cli\24-react-redux组件间数据共享\redux\actions\count.js
import { INCREMENT, DECREMENT } from "../constant";
// 该文件专门为count组件生成action对象

// 同步 action 也就是 action 值为对象
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
// 异步 action 也就是 action 值为函数,异步 action 不是必须要用的
export const createIncrementAsyncAction = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, time);
  };
};

// 03-React-cli\24-react-redux组件间数据共享\redux\actions\person.js
import { ADD_PERSON } from "../constant";

export const createAddPersonAction = (personObj) => ({
  type: ADD_PERSON,
  data: personObj,
});
reducers
// 03-React-cli\24-react-redux组件间数据共享\redux\reducers\count.js
import { INCREMENT, DECREMENT } from "../constant";

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  // console.log(type, data);
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      return preState;
  }
}


// 03-React-cli\24-react-redux组件间数据共享\redux\reducers\person.js

import { ADD_PERSON } from "../constant";

// 初始化人的列表
const initState = [];

export default function personReducer(preState = initState, action) {
  const { type, data } = action;
  switch (type) {
    case ADD_PERSON:
      return [{ ...data, id: new Date().getTime() }, ...preState];
    // return initState.push({ ...data, id: new Date().getTime() });
    default:
      return preState;
  }
}

03-React-cli\24-react-redux 组件间数据共享\redux\constant.js
// 该模块用于定义action对象中type类型的值
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
export const ADD_PERSON = "add_person";
03-React-cli\24-react-redux 组件间数据共享\redux\store.js
// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware, combineReducers } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入为 count 组件服务的 reducer
import countReducer from "./reducers/count";
// 引入为 person 组件服务的 reducer
import personReducer from "./reducers/person";
// 合并 reducers
const allReduces = combineReducers({
  count: countReducer,
  person: personReducer,
});
export default createStore(allReduces, applyMiddleware(thunk));

25-react-redux 开发者工具

03-React-cli\25-react-redux 开发者工具\redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware, combineReducers } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入为 count 组件服务的 reducer
import countReducer from "./reducers/count";
// 引入为 person 组件服务的 reducer
import personReducer from "./reducers/person";

// 引入 redux-devtools-extension
import { composeWithDevTools } from "redux-devtools-extension";

// 合并 reducers
const allReduces = combineReducers({
  count: countReducer,
  person: personReducer,
});
export default createStore(
  allReduces,
  composeWithDevTools(applyMiddleware(thunk))
);

26-react-redux 最终版

03-React-cli\26-react-redux 最终版\containers

// 03-React-cli\26-react-redux最终版\containers\Count\index.jsx

// 定义CountUI组件
import React, { Component } from "react";

import {
  increment,
  decrement,
  incrementAsync,
} from "../../redux/actions/count";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.increment(value - 0);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.decrement(value - 0);
  };
  incrementIfOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.increment(value - 0);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.incrementAsync(value - 0, 500);
  };
  render() {
    return (
      <div>
        <h4>我是 Count 组件,下方组件人数为{this.props.persons.length}</h4>
        <h5>当前求和值为: {this.props.count}</h5>
        <select style={{ width: "100px" }} ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        &nbsp;
        <button onClick={this.increment}>+</button> &nbsp;
        <button onClick={this.decrement}>-</button> &nbsp;
        <button onClick={this.incrementIfOdd}>当前求和为奇数再加</button> &nbsp;
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

// 使用 connect 创建并暴露一个 CountUI 的容器组件
// 传两个值
// 1. redux 中所保存的数据
// 2. 用于操作状态的方法
export default connect(
  (state) => ({
    persons: state.persons,
    count: state.count
  }),

  // mapStateToProps 一般写法
  // (dispatch) => ({
  //   increment: (data) => dispatch(createIncrementAction(data)), // 通知 redux 执行加法
  //   decrement: (data) => dispatch(createDecrementAction(data)), // 通知 redux 执行加法
  //   incrementAsync: (data, time) =>
  //     dispatch(createIncrementAsyncAction(data, time)), // 通知 redux 执行加法
  // })

  // mapStateToProps 简易写法
  {
    increment,
    decrement,
    incrementAsync,
  }
)(Count);



// 03-React-cli\26-react-redux最终版\containers\Person\index.jsx
import React, { Component } from "react";

// 引入 connect 用于连接 store 和 UI组件
import { connect } from "react-redux";

import { AddPerson } from "../../redux/actions/person";
class Person extends Component {
  AddPerson = () => {
    const name = this.nameNode.value;
    const age = this.ageNode.value;
    this.props.AddPerson({ name, age });
    this.nameNode.value = this.ageNode.value = "";
  };
  render() {
    return (
      <div>
        <h4>我是 Person 组件,上方组件求和为{this.props.count}</h4>
        <input
          ref={(c) => (this.nameNode = c)}
          type="text"
          placeholder="输入名字"
        />
        <input
          ref={(c) => (this.ageNode = c)}
          type="text"
          placeholder="输入年龄"
        />
        <button onClick={this.AddPerson}>添加</button>
        <ul>
          {this.props.persons.map((el) => {
            return (
              <li key={el.id}>
                名字{el.name},年龄{el.age}
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default connect(
  (state) => ({
    persons: state.persons,
    count: state.count,
  }),
  {
    AddPerson,
  }
)(Person);

03-React-cli\26-react-redux 最终版\redux\actions

// 03-React-cli\26-react-redux最终版\redux\actions\count.js

import { INCREMENT, DECREMENT } from "../constant";
// 该文件专门为count组件生成action对象

// 同步 action 也就是 action 值为对象
export const increment = (data) => ({ type: INCREMENT, data });
export const decrement = (data) => ({ type: DECREMENT, data });
// 异步 action 也就是 action 值为函数,异步 action 不是必须要用的
export const incrementAsync = (data, time) => {
  return (dispatch) => {
    setTimeout(() => {
      dispatch(increment(data));
    }, time);
  };
};

// 03-React-cli\26-react-redux最终版\redux\actions\person.js
import { ADD_PERSON } from "../constant";

export const AddPerson = (personObj) => ({
  type: ADD_PERSON,
  data: personObj,
});

03-React-cli\26-react-redux 最终版\redux\reducers

// 03-React-cli\26-react-redux最终版\redux\reducers\count.js

import { INCREMENT, DECREMENT } from "../constant";

/**
 * 1. 该文件是用于创建一个为 count 组件服务的 reducer, 本质就是一个函数;
 * 2. reducer函数会接收两个参数,分别是:之前的状态 preState, 动作对象 action
 */
const initState = 0;
export default function countReducer(preState = initState, action) {
  const { type, data } = action;
  // console.log(type, data);
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      return preState;
  }
}



// 03-React-cli\26-react-redux最终版\redux\reducers\count.js
import { ADD_PERSON } from "../constant";
// 初始化人的列表
const initState = [];
export default function personReducer(preState = initState, action) {
  const { type, data } = action;
  // 在这里return 的时候,如果返回同一个数据,那么redux是不去更新页面的
  switch (type) {
    case ADD_PERSON:
      return [{ ...data, id: new Date().getTime() }, ...preState];
    default:
      return preState;
  }
}






// 03-React-cli\26-react-redux最终版\redux\reducers\index.js
// 该文件用于汇总所有的 reduce
// 引入createStore,专门用于创建store对象
import { combineReducers } from "redux";
// 引入为 count 组件服务的 reducer
import count from "./count";
// 引入为 person 组件服务的 reducer
import persons from "./person";

// 合并 reducers
export default combineReducers({
  count,
  persons,
});





03-React-cli\26-react-redux 最终版\redux\constant.js

// 该模块用于定义action对象中type类型的值
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
export const ADD_PERSON = "add_person";

03-React-cli\26-react-redux 最终版\redux\store.js

// 该文件专门用于暴露一个store对象, 整个应用只有一个store;

// 引入createStore,专门用于创建store对象
import { createStore, applyMiddleware } from "redux";
// 引入 redux-thunk 用于支持 异步 action
import thunk from "redux-thunk";
// 引入 redux-devtools-extension
import { composeWithDevTools } from "redux-devtools-extension";
// 引入所有的 reducers
import allReduces from "./reducers/index";

export default createStore(
  allReduces,
  composeWithDevTools(applyMiddleware(thunk))
);
03-React-cli\26-react-redux 最终版\App.jsx
//创建外壳组件APP
import React, { Component } from "react";
import Count from "./containers/Count";
import Person from "./containers/Person";
export default class App extends Component {
  render() {
    return (
      <div>
        <Count />
        <hr />
        <Person />
      </div>
    );
  }
}

03-React-cli\26-react-redux 最终版\index.js

//引入核心库
import React from "react";
import ReactDOM from "react-dom";
//引入组件
import App from "./App.jsx";
import store from "./redux/store.js";
import { Provider } from "react-redux";

// 检测 redux 中状态的变化,变化了就重新渲染页面,重新render一下

// 但是,用上 react-redux 之后就不需要这个了
// store.subscribe(() => {
//   ReactDOM.render(
//     <BrowserRouter>
//       <App />
//     </BrowserRouter>,
//     document.getElementById("root")
//   );
// });

ReactDOM.render(
  // 在这里这样写的话就不需要每个组件都传 store 了
  // 此处需要 Provider 包裹一下 App,目的是为了 App 后代都可以接收到 store
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

React 扩展

01-setState

import React, { Component } from "react";

export default class Demo1 extends Component {
  state = { count: 0 };
  add = () => {
    const { count } = this.state;
    // 第一种写法,(对象式的 setState)
    // setState是一个同步的方法,
    // 但是setState引起的React后续更新的动作是异步的,可以回调处理
    // this.setState(
    //   {
    //     count: count + 1,
    //   },
    //   () => {
    //     console.log("我改完了"); // 后打印
    //   }
    // );
    // console.log("我改完了么"); // 先打印

    // 第一种写法,(函数式的 setState)
    this.setState(
      (state) => ({ count: state.count + 1 }),
      () => {
        console.log("我改完了"); // 后打印
      }
    );
    console.log("我改完了么"); // 先打印
  };
  render() {
    return (
      <div>
        <h1>当前求和: {this.state.count}</h1>
        <button onClick={this.add}>点我加1 </button>
      </div>
    );
  }
}

02-lazyLoad

//创建外壳组件APP
import React, { Component, lazy, Suspense } from "react";
import { NavLink, Route } from "react-router-dom";
// import About from "./About";
// import Home from "./Home";
const Home = lazy(() => import("./Home"));
const About = lazy(() => import("./Home"));
export default class Demo2 extends Component {
  render() {
    return (
      <div>
        <h1>我是APP</h1>
        {/* 一个项目只允许有一个 BrowserRoute,把这个写到index.js里面 */}
        {/* 注册跳转标签 */}
        <NavLink to="/about" activeClassName="active">
          {" "}
          About{" "}
        </NavLink>
        <NavLink to="/home"> Home </NavLink>
        {/* 注册路由 */}
        <Suspense fallback={<h2>Loading..</h2>}>
          <Route path="/about" component={About} />
          <Route path="/home" component={Home} />
        </Suspense>
      </div>
    );
  }
}

03-hooks

import React, { Component, useState } from "react";
import ReactDOM from "react-dom";
// 类式组件
// export default class Demo3 extends Component {
//   state = { count: 1, timer: null };
//   myRef = React.createRef();
//   add = () => {
//     this.setState((state) => ({ count: state.count + 1 }));
//   };
//   show = () => {
//     alert(this.myRef.current.value);
//   };
//   unmount = () => {
//     ReactDOM.unmountComponentAtNode(document.getElementById("root"));
//   };
//   componentDidMount() {
//     this.state.timer = setInterval(() => {
//       this.setState((state) => ({ count: state.count + 1 }));
//     }, 1000);
//   }
//   componentWillUnmount() {
//     clearInterval(this.state.timer);
//   }
//   render() {
//     return (
//       <div>
//         <input type="text" ref={this.myRef} />
//         <h2>{this.state.count}</h2>
//         <button onClick={this.add}>点我+1</button>
//         <button onClick={this.unmount}>卸载组件</button>
//         <button onClick={this.show}>点击提示</button>
//       </div>
//     );
//   }
// }

// 函数式组件
function Demo3() {
  // useState
  // state 每次的变化都会重新进到这里面
  // 但是 React 内部处理了,不会每次都把 count 重置
  const [count, setCount] = React.useState(0);
  function add() {
    // setCount(count + 1);     // 第一种写法
    setCount((count) => count + 1); // 第二种写法
  }
  function unmount() {
    ReactDOM.unmountComponentAtNode(document.getElementById("root"));
  }
  // useEffect
  React.useEffect(() => {
    // 这个函数相当于 componentDidMount
    let timer = setInterval(() => {
      setCount((count) => count + 1);
    }, 1000);
    // 这个返回的函数  相当于 componentWillUnmount
    return () => {
      clearInterval(timer);
    };
  }, []);

  const myRef = React.useRef();
  function show() {
    alert(myRef.current.value);
  }
  return (
    <div>
      <input type="text" ref={myRef} />
      <h2>求和:{count}</h2>
      <button onClick={add}>点我+1</button>
      <button onClick={unmount}>卸载组件</button>
      <button onClick={show}>点我提示数据</button>
    </div>
  );
}
export default Demo3;

04-Fragment

import React, { Component, Fragment } from "react";

export default class Demo4 extends Component {
  render() {
    return <Fragment key={1}>1111</Fragment>; // 需要加属性的时候这个 Fragment 就很好用了
    // <div id="root"><div>1111</div></div>

    // return <div>1</div>;
    // <div id="root"><div><div>1</div></div></div>
  }
}

05-Context

import React, { Component } from "react";
// 创建 createContext 对象
const UsernameContext = React.createContext();
const { Provider, Consumer } = UsernameContext;
class B extends Component {
  render() {
    return (
      <div style={{ border: "1px solid blue", margin: "20px" }}>
        <h3>我是B组件</h3>
        <h4>我的从A接收到的用户名是</h4>
        <C />
      </div>
    );
  }
}
// C 类式组件
// class C extends Component {
//   // 1. 声明接收 contextType
//   static contextType = UsernameContext;
//   render() {
//     console.log(this.context);
//     return (
//       <div style={{ border: "1px solid green", margin: "20px" }}>
//         <h3>我是C组件</h3>
//         <h4>我的从A接收到的用户名是 {this.context.name}</h4>
//         <h4>我的从A接收到的年龄是 {this.context.age}</h4>
//       </div>
//     );
//   }
// }
// C 函数式组件
function C(params) {
  return (
    <div style={{ border: "1px solid green", margin: "20px" }}>
      <h3>我是C组件</h3>
      <h4>
        我的从A接收到的用户名是
        <Consumer>
          {(value) => {
            return value.name;
          }}
        </Consumer>
      </h4>
      <h4>
        我的从A接收到的年龄是
        <Consumer>
          {(value) => {
            return value.age;
          }}
        </Consumer>
      </h4>
    </div>
  );
}

export default class A extends Component {
  state = { name: "YO", age: 12 };
  render() {
    const { name, age } = this.state;
    return (
      <div style={{ border: "1px solid red", margin: "20px" }}>
        <h3>我是A组件</h3>
        <h4>我的用户名是 {this.state.name}</h4>
        <Provider value={{ name, age }}>
          <B />
        </Provider>
      </div>
    );
  }
}

06-optimize

import React, { Component, PureComponent } from "react";

export default class Parent extends PureComponent {
  state = { carName: "别克", stus: ["1", "2"] };
  changeCar = () => {
    // 这样写就不行了
    // const obj = this.state;
    // obj.carName = "迈巴赫";
    // this.setState(obj);
    //
    //
    // 这样写是生效的
    // this.setState({
    //   carName: "迈巴赫",
    // });
  };
  addStu = () => {
    // 这样写是无效的
    // const { stus } = this.state;
    // stus.unshift("3");
    // this.setState({stus});
    //
    //
    // 这样写是生效的
    const { stus } = this.state;
    this.setState({ stus: ["3", ...stus] });
  };
  //   shouldComponentUpdate(nextProps, nextState) {
  //     return !(this.state.carName === nextState.carName);
  //   }
  render() {
    const { carName, stus } = this.state;
    console.log("Parent---------------------");
    return (
      <div style={{ border: "1px solid blue", margin: "20px" }}>
        <h3>我是 Parent 组件</h3>
        <h4>我的车名字是 {carName}</h4>
        <button onClick={this.changeCar}>换车</button>
        <button onClick={this.addStu}>添加学生</button>
        {stus.map((el) => {
          return <span key={el}>{el}---</span>;
        })}
        {/* <Child /> */}
        <Child carName={carName} />
      </div>
    );
  }
}
class Child extends PureComponent {
  //   shouldComponentUpdate(nextProps, nextState) {
  //     return !(this.props.carName === nextProps.carName);
  //   }
  render() {
    console.log("Child---------------------");
    return (
      <div style={{ border: "1px solid red", margin: "20px" }}>
        <h3>我是 Child 组件</h3>
        <h4>我接到的车是 {this.props.carName}</h4>
      </div>
    );
  }
}

07-renderProps

import React, { Component } from "react";

export default class Parent extends Component {
  render() {
    return (
      <div style={{ border: "1px solid green", margin: "20px" }}>
        <h2>Parent</h2>
        {/* <A>
          <B />
        </A> */}
        <A render={(name) => <B name={name} />} />
      </div>
    );
  }
}
class A extends Component {
  state = { name: "tom" };
  render() {
    console.log(this.props.children);
    const { name } = this.state;
    return (
      <div style={{ border: "1px solid green", margin: "20px" }}>
        <h2>A</h2>
        {this.props.render(name)}
      </div>
    );
  }
}

class B extends Component {
  render() {
    console.log("B---->render");
    return (
      <div style={{ border: "1px solid green", margin: "20px" }}>
        <h2>B</h2>
        <h3>从上层父组件拿到的名字: {this.props.name}</h3>
      </div>
    );
  }
}

08-ErrorBoundary

Parent.jsx

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

export default class Parent extends Component {
  state = {
    hasError: "", // 用于标识子组件是否产生错误
  };
  static getDerivedStateFromError(error) {
    console.log(error);
    return {
      hasError: error,
    };
  }
  // 子组件渲染错误的时候会执行这里
  // 一般用于收集错误,反馈给服务器,用于通知编码人员进行问题修复
  componentDidCatch(error, info) {
    console.log(error, info);
    console.log("渲染错误");
  }
  render() {
    return (
      <div>
        <h2>我是 Parent 组件</h2>
        {this.state.hasError ? (
          <h2>当前网络不稳定...请稍后重试....</h2>
        ) : (
          <Child />
        )}
      </div>
    );
  }
}

Child.jsx

import React, { Component } from "react";

export default class Child extends Component {
  state = {
    // users: [
    //   { id: 1, name: 1, age: 1 },
    //   { id: 2, name: 2, age: 2 },
    //   { id: 3, name: 3, age: 3 },
    // ],
    users: "aaa",
  };
  render() {
    return (
      <div>
        <h2>我是 Child 组件</h2>
        {this.state.users.map((user) => {
          return (
            <h4 key={user.id}>
              {user.name}-----{user.age}
            </h4>
          );
        })}
      </div>
    );
  }
}

React Router 6

1.<BrowserRouter>
    1.说明:<BrowserRouter>用于包裹整个应用.
    2.示例代码:
        import React from "react";
        import ReactDOM from "react-dom";
        import { BrowserRouter } from "react-router-dom";
        ReactDOM.render(
            <BrowserRouter>
                {/_ 整体结构 (通常为 App 组件) _/}
            </BrowserRouter>,
        root)

2.<HashRouter>
    1. 说明: 作用与<BrowserRouter>一样,但<HashRouter>修改的是地址栏的 hash 值.
    2. 备注: 6.x 版本中<HashRouter><BrowserRouter>的用法与 5.x 相同

3.<Routes/> 与 <Route/>
    1.v6 版本中移出了先前的<Switch>,引入了新的替代者: <Routes>.
    2.<Routes>和 <Route>要配合使用,且必须要用<Routes>包裹<Route>.
    3.<Route>相当于一个 if 语句,如果其路径与当前 URL 匹配,则呈现其对应的组件
    4.<Route caseSensitive> 属性用于指定: 匹配时是否区分大小写 (默认为 false).
    5.当 URL 发生变化时,<Routes>都会查看其所有子<Route> 元素以找到最佳匹配并呈现组件
    6.<Route>也可以嵌套使用,且可配合 useRoutes() 配置"路由表",但需要通过<outlet>组件来渲染其子路由.
    7. 示例代码
    <Routes>
        /*path属性用于定义路径,element属性用于定义当前路径所对应的组件*/
        <Route path="/login" element={<Login />}></Route>
        *用于定义嵌套路由,home是一级路由,对应的路径/home*/
        <Route path="home" element={<Home />}>
            /*test1 和 test2 是二级路由,对应的路径是/home/test1 或 /home/test2*/
            <Route path="test1" element={<Test/>}></Route>
            <Route path="test2" element={<Test2/>}></Route>
        </Route>
        //Route也可以不写element届性,这时就是用于展示嵌套的路由.所对应的路径是/users/xxx
        <Route path="users">
            <Route path="xxx" element={<Demo />} />
        </Route>
    </Routes>


4. <Link>
    1.作用: 修改URL,且不发送网络请求 (路由链接)
    2.注意:外侧需要用<BrowserRouter>或<HashRouter>包裹
    3. 示例代码:
        import { Link } from "react-router-dom";
        function Test() {
            return (
                <div>
                    <Link to="/路径">按钮</Link>
                </div>
            )
        }


5. <NavLink>
    1.作用: 与<Link>组件类似,且可实现导航的"高亮"效果
    2. 示例代码:
        // 注意: NavLink默认类名是active,下面是指定自定义的class
        //自定义样式
        <NavLink
            to="login"
            className={({ isActive }) => {
                console.log('home',isActive)
                return isActive ? 'base one' :'base'
            >login</NavLink>

        // 默认情况下,当Home的子组件匹配成功,Home的导航也会高亮,
        // 当NavLink上添加了end属性后,若Home的子组件匹配成功,则Home的导航没有高亮效果
        <NavLink to="home" end >home</NavLink>


6. <Navigate>
    1.作用:只要<Navigate>组件被染,就会修改路径,切换视图.
    2.replace 属性用于控制跳转模式 (push 或 replace,默认是push).
    3.示例代码:
        import React,fuseStater from 'react!
        import {Navigate} from 'react-router-dom!
        export default function Home() {
            const [sum,setSum] = useState(1)
            return (
                <div>
                    <h3>我是Home的内容</h3>
                    // 根据sum的值决定是否切换视图
                    {sum === 1 ?<h4>sum的值为{sum}</h4> : <Navigate to="/about" replace={true}/>}
                    <button onClick={()=>setSum(2)}>点我将sum变为2</button>
                </div>
                )
        }


6. <Outlet>
1. useRoutes()

2. useNavigate()

3. useParams()

4. useSearchParams()

5. useLocation()

6. useMatch()

7. uselnRouterContext()

8. useNavigationType()
    1.作用: 返回当前的导航类型 (用户是如何来到当前页面的)
    2.返回值: POPPUSHREPLACE
    3.备注: PoP 是指在浏览器中直接打开了这个路由组件 (刷新页面)
9. useOutlet()
    1.作用: 用来呈现当前组件中渲染的嵌套路由
    2.示例代码:
        const result = useOutlet()
        console.log(result)
        // 如果嵌套路由没有挂载,则result为nu11
        // 如果嵌套路由已经挂载,则展示嵌套的路由对象
10. useResolvedPath()
    1.作用: 给定一个 URL值,解析其中的: path、search、hash值.

01-一级路由

//创建外壳组件APP
import React, { Component } from "react";
import { NavLink, Route, Routes, Navigate, useRoutes } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
import routers from "./routers";
export default function App() {
  return (
    <div>
      <h1>我是APP</h1>
      {/* 注册跳转标签 */}
      <NavLink to="/about">About</NavLink>
      <NavLink to="/home">Home</NavLink>
      {/* 注册路由 普通写法 */}
      {/* 之前 Switch 现在是 Routes */}

      <Routes>
        {/* 之前是 Component 现在是 element */}
        {/* 现在 path 是大小写都可以了 about ABOUT 是一样的 都会被解析成小写 */}
        <Route path="/about" element={<About />} />
        <Route path="/home" element={<Home />} />

        {/* Routes 和之前一样,一个匹配上后就不会去匹配别的了 */}
        <Route path="/home" element={<Demo />} />
      </Routes>
    </div>
  );
}

02-Navigate 重定向

//创建外壳组件APP
import React, { Component } from "react";
import { NavLink, Route, Routes, Navigate, useRoutes } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
export default function App() {
  return (
    <div>
      <h1>我是APP</h1>
      {/* 注册跳转标签 */}
      <NavLink to="/about">About</NavLink>
      <NavLink to="/home">Home</NavLink>
      {/* 注册路由 普通写法 */}
      {/* 之前 Switch 现在是 Routes */}

      <Routes>
        {/* 之前是 Component 现在是 element */}
        {/* 现在 path 是大小写都可以了 about ABOUT 是一样的 都会被解析成小写 */}
        <Route path="/about" element={<About />} />
        <Route path="/home" element={<Home />} />

        {/* Routes 和之前一样,一个匹配上后就不会去匹配别的了 */}
        <Route path="/home" element={<Demo />} />

        {/* Navigate 和之前的 redirect 效果一样,写法不一样 */}
        <Route path="/" element={<Navigate to="/about" />} />
      </Routes>
    </div>
  );
}
import React, { Component, useState } from "react";
import { Navigate } from "react-router-dom";
export default function Home() {
  const [sum, setSum] = useState(0);
  return (
    <div style={{ padding: "10px" }}>
      <div style={{ border: "1px solid red" }}>
        <h2>导航区</h2>
        <div>
          <a style={{ float: "left", marginRight: "10px" }}>About</a>
          <a style={{ color: "red" }}>Home</a>
        </div>
      </div>
      <div style={{ border: "1px solid green", marginTop: "10px" }}>
        <h2>展示区</h2>
        <h2>Home</h2>

        {/* 这样样式一个 Navigate 的使用方法 */}
        {/* Navigate 只要渲染就会引起页面的切换 */}
        {sum === 2 ? (
          <Navigate to="/about" replace />
        ) : (
          <h4>当前的sum的值为{sum}</h4>
        )}
        <button onClick={() => setSum(2)}>点击改变sum的值</button>
      </div>
    </div>
  );
}

03-NavLink 高亮

//创建外壳组件APP
import React, { Component } from "react";
import { NavLink, Route, Routes, Navigate, useRoutes } from "react-router-dom";
import About from "./pages/About";
import Home from "./pages/Home";
export default function App() {
  return (
    <div>
      <h1>我是APP</h1>
      {/* 注册跳转标签 */}
      {/* 之前是 activeClassName 现在是 className 返回一个函数 */}
      <NavLink
        className={({ isActive }) => (isActive ? "lgqactive" : "aaa")}
        to="/about"
      >
        About
      </NavLink>
      <NavLink
        className={({ isActive }) => (isActive ? "lgqactive" : "aaa")}
        to="/home"
      >
        Home
      </NavLink>
      {/* 注册路由 普通写法 */}
      {/* 之前 Switch 现在是 Routes */}

      <Routes>
        {/* 之前是 Component 现在是 element */}
        {/* 现在 path 是大小写都可以了 about ABOUT 是一样的 都会被解析成小写 */}
        <Route path="/about" element={<About />} />
        <Route path="/home" element={<Home />} />
      </Routes>
    </div>
  );
}

04-useRoutes 路由表

04-useRoutes 路由表\App.jsx

//创建外壳组件APP
import React, { Component } from "react";
import { NavLink, useRoutes } from "react-router-dom";

import routers from "./routers";
export default function App() {
  // 这个就是路由表,来生成路由
  const element = useRoutes(routers);
  return (
    <div>
      <h1>我是APP</h1>
      {/* 注册跳转标签 */}
      {/* 之前是 activeClassName 现在是 className 返回一个函数 */}
      <NavLink to="/about">About</NavLink>
      <NavLink to="/home">Home</NavLink>

      {/* 路由表写法 */}
      {element}
    </div>
  );
}

04-useRoutes 路由表\routers\index.js

import { Navigate } from "react-router-dom";
import About from "../pages/About";
import Home from "../pages/Home";
export default [
  {
    path: "/about",
    element: <About />,
  },
  {
    path: "/home",
    element: <Home />,
  },
  {
    path: "/",
    element: <Navigate to="/about" />,
  },
];

05-Outlet 组件展示位置

import React, { Component, useState } from "react";
import { NavLink, Outlet } from "react-router-dom";

export default function Home() {
  return (
    <div style={{ padding: "10px" }}>
      <div style={{ border: "1px solid red" }}>
        <h2>导航区</h2>
        <div>
          <a style={{ float: "left", marginRight: "10px" }}>About</a>
          <a style={{ color: "red" }}>Home</a>
        </div>
      </div>
      <div style={{ border: "1px solid green", marginTop: "10px" }}>
        <h2>展示区</h2>
        <h2>Home</h2>
        <NavLink
          to="message"
          className={({ isActive }) => (isActive ? "lgqactive" : "")}
        >
          Message
        </NavLink>
        <NavLink
          to="news"
          className={({ isActive }) => (isActive ? "lgqactive" : "")}
        >
          News
        </NavLink>
        {/* 指定路由组件呈现的位置, 类似Vue的 route-view */}
        <Outlet />
      </div>
    </div>
  );
}

06-路由的 params 参数

06-路由的 params 参数\routers\index.js
import { Navigate } from "react-router-dom";
import About from "../pages/About";
import Home from "../pages/Home";
import Message from "../pages/Home/Message";
import News from "../pages/Home/News";
import Detail from "../pages/Home/Message/Detail";
export default [
  {
    path: "/about",
    element: <About />,
  },
  {
    path: "/home",
    element: <Home />,
    children: [
      {
        path: "message",
        element: <Message />,
        children: [
          {
            path: "detail/:id/:title/:content", // 路由 params 参数
            element: <Detail />,
          },
        ],
      },
      {
        path: "news",
        element: <News />,
      },
    ],
  },
  {
    path: "/",
    element: <Navigate to="/about" />,
  },
];
06-路由的 params 参数\pages\Home\Message\index.jsx
import React, { Component, useState } from "react";
import { Link, NavLink, Outlet } from "react-router-dom";

export default function Message() {
  const [message] = useState([
    { id: 1, title: "消息1", content: "内容1" },
    { id: 2, title: "消息2", content: "内容2" },
    { id: 3, title: "消息3", content: "内容3" },
    { id: 4, title: "消息4", content: "内容4" },
  ]);
  return (
    <div>
      <ul>
        {message.map((el) => {
          return (
            <li key={el.id}>
              {/* 路由 params 参数 */}
              <Link to={`detail/${el.id}/${el.title}/${el.content}`}>
                {el.title}
              </Link>
            </li>
          );
        })}
      </ul>
      <hr />
      {/* 指定 Detail 的展示位置 */}
      <Outlet />
    </div>
  );
}
06-路由的 params 参数\pages\Home\Message\Detail\index.jsx
import React from "react";
import { useParams } from "react-router-dom";
export default function Detail() {
  // 接收 params 参数
  const { id, title, content } = useParams();
  const _history = useMatch("/home/message/detail/:id/:title/:content");
  // 这个 _history 就是我们之前 Router5 的时候可以拿到的东西
  return (
    <div>
      <span>{id}</span> -<span>{title}</span> -<span>{content}</span>
    </div>
  );
}

07-路由的 search 参数

07-路由的 search 参数\routers\index.js

import { Navigate } from "react-router-dom";
import About from "../pages/About";
import Home from "../pages/Home";
import Message from "../pages/Home/Message";
import News from "../pages/Home/News";
import Detail from "../pages/Home/Message/Detail";
export default [
  {
    path: "/about",
    element: <About />,
  },
  {
    path: "/home",
    element: <Home />,
    children: [
      {
        path: "message",
        element: <Message />,
        children: [
          {
            path: "detail", // 路由 search 参数
            element: <Detail />,
          },
        ],
      },
      {
        path: "news",
        element: <News />,
      },
    ],
  },
  {
    path: "/",
    element: <Navigate to="/about" />,
  },
];

07-路由的 search 参数\pages\Home\Message\index.jsx

import React, { Component, useState } from "react";
import { Link, NavLink, Outlet } from "react-router-dom";

export default function Message() {
  const [message] = useState([
    { id: 1, title: "消息1", content: "内容1" },
    { id: 2, title: "消息2", content: "内容2" },
    { id: 3, title: "消息3", content: "内容3" },
    { id: 4, title: "消息4", content: "内容4" },
  ]);
  return (
    <div>
      <ul>
        {message.map((el) => {
          return (
            <li key={el.id}>
              {/* 路由 search 参数 */}
              <Link
                to={`detail?id=${el.id}&title=${el.title}&content=${el.content}&`}
              >
                {el.title}
              </Link>
            </li>
          );
        })}
      </ul>
      <hr />
      {/* 指定 Detail 的展示位置 */}
      <Outlet />
    </div>
  );
}

07-路由的 search 参数\pages\Home\Message\Detail\index.jsx

import React from "react";
import { useLocation, useSearchParams } from "react-router-dom";
export default function Detail() {
  // 接收 search 参数
  const [search, setSearch] = useSearchParams();
  const id = search.get("id");
  const title = search.get("title");
  const content = search.get("content");
  const _history = useLocation();
  console.log(_history);
  return (
    <div>
      <button onClick={() => setSearch("id=123&title=123&content=123")}>
        点我更新收到的 search 参数
      </button>
      <span>{id}</span> -<span>{title}</span> -<span>{content}</span>
    </div>
  );
}

08-路由的 state 参数

08-路由的 state 参数\routers\index.js

import { Navigate } from "react-router-dom";
import About from "../pages/About";
import Home from "../pages/Home";
import Message from "../pages/Home/Message";
import News from "../pages/Home/News";
import Detail from "../pages/Home/Message/Detail";
export default [
  {
    path: "/about",
    element: <About />,
  },
  {
    path: "/home",
    element: <Home />,
    children: [
      {
        path: "message",
        element: <Message />,
        children: [
          {
            path: "detail", // 路由 state 参数
            element: <Detail />,
          },
        ],
      },
      {
        path: "news",
        element: <News />,
      },
    ],
  },
  {
    path: "/",
    element: <Navigate to="/about" />,
  },
];

08-路由的 state 参数\pages\Home\Message\index.jsx

import React, { Component, useState } from "react";
import { Link, NavLink, Outlet } from "react-router-dom";

export default function Message() {
  const [message] = useState([
    { id: 1, title: "消息1", content: "内容1" },
    { id: 2, title: "消息2", content: "内容2" },
    { id: 3, title: "消息3", content: "内容3" },
    { id: 4, title: "消息4", content: "内容4" },
  ]);
  return (
    <div>
      <ul>
        {message.map((el) => {
          return (
            <li key={el.id}>
              {/* 路由 search 参数 */}
              <Link
                to="detail"
                state={{
                  ...el,
                }}
              >
                {el.title}
              </Link>
            </li>
          );
        })}
      </ul>
      <hr />
      {/* 指定 Detail 的展示位置 */}
      <Outlet />
    </div>
  );
}

08-路由的 state 参数\pages\Home\Message\Detail\index.jsx

import React from "react";
import { useLocation } from "react-router-dom";
export default function Detail() {
  // 接收 state 参数
  const stateCfg = useLocation();
  const {
    state: { id, title, content },
  } = stateCfg;
  return (
    <div>
      <span>{id}</span> -<span>{title}</span> -<span>{content}</span>
    </div>
  );
}

09-编程式路由

09-编程式路由\pages\Home\Message\index.jsx

import React, { Component, useState } from "react";
import { Link, useNavigate, Outlet } from "react-router-dom";

export default function Message() {
  const [message] = useState([
    { id: 1, title: "消息1", content: "内容1" },
    { id: 2, title: "消息2", content: "内容2" },
    { id: 3, title: "消息3", content: "内容3" },
    { id: 4, title: "消息4", content: "内容4" },
  ]);
  const navigate = useNavigate();
  function showDetail(message) {
    // navigate('/about')
    // 但是去子路由的时候就不能带斜杠了
    // 目前 navigate 携带参数只能携带 state 参数,search 和 params 参数不可以携带
    navigate("detail", {
      replace: true,
      state: message,
    });
  }
  return (
    <div>
      <ul>
        {message.map((el) => {
          return (
            <li key={el.id}>
              {/* 路由 search 参数 */}
              <Link
                to="detail"
                state={{
                  ...el,
                }}
              >
                {el.title}
              </Link>
              <button onClick={() => showDetail(el)}>查看详情</button>
            </li>
          );
        })}
      </ul>
      <hr />
      {/* 指定 Detail 的展示位置 */}
      <Outlet />
    </div>
  );
}

09-编程式路由\pages\Home\Message\Detail\index.jsx

import React from "react";
import { useLocation } from "react-router-dom";
export default function Detail() {
  // 接收 state 参数
  const stateCfg = useLocation();
  const {
    state: { id, title, content },
  } = stateCfg;
  return (
    <div>
      <span>{id}</span> -<span>{title}</span> -<span>{content}</span>
    </div>
  );
}

10-uselnRouterContext

10-useInRouterContext\components\Demo.jsx

import React from "react";
import { useInRouterContext } from "react-router-dom";

export default function Demo() {
  console.log("demo --- useInRouterContext", useInRouterContext()); // demo --- useInRouterContext false
  return <div>Demo</div>;
}

10-useInRouterContext\components\Header.jsx

import React from "react";
import { useNavigate, useInRouterContext } from "react-router-dom";

export default function Header() {
  const navigate = useNavigate();
  // 只要是在路由内 useInRouterContext() 就为 true
  console.log("header --- useInRouterContext", useInRouterContext()); // header --- useInRouterContext true
  function back(params) {
    navigate(-1);
  }
  function forward(params) {
    navigate(1);
  }
  return (
    <div>
      <button onClick={back}>后退</button>
      <button onClick={forward}>前进</button>
    </div>
  );
}

11-useNavigationType

import React, { Component } from "react";
import { useNavigationType } from "react-router-dom";

export default function News() {
  // POP 刷新
  // PUSH
  // REPLACE
  // 三种情况,我们这里是用的 PUSH
  console.log("News --- useNavigationType", useNavigationType());

  return (
    <div>
      <ul>
        <li>News 1</li>
        <li>News 2</li>
        <li>News 3</li>
        <li>News 3</li>
      </ul>
    </div>
  );
}

12-useOutlet

import React, { Component, useState } from "react";
import { NavLink, Outlet, useOutlet } from "react-router-dom";

export default function Home() {
  // 如果嵌套路由没有挂载,则result为nu11
  // 如果嵌套路由已经挂载,则展示嵌套的路由对象
  console.log("Home --- useOutlet", useOutlet());
  return (
    <div style={{ padding: "10px" }}>
      <div style={{ border: "1px solid red" }}>
        <h2>导航区</h2>
        <div>
          <a style={{ float: "left", marginRight: "10px" }}>About</a>
          <a style={{ color: "red" }}>Home</a>
        </div>
      </div>
      <div style={{ border: "1px solid green", marginTop: "10px" }}>
        <h2>展示区</h2>
        <h2>Home</h2>
        <NavLink to="message">Message</NavLink>
        <NavLink to="news">News</NavLink>
        {/* 指定路由组件呈现的位置, 类似Vue的 route-view */}
        <Outlet />
      </div>
    </div>
  );
}

13-useResolvedPath

import React, { Component } from "react";
import { useResolvedPath } from "react-router-dom";
export default function News() {
  console.log(
    "useResolvedPath",
    useResolvedPath("/user?id=001&name=tom#qwert")
  );
  // {
  //     "pathname": "/user",
  //     "search": "?id=001&name=tom",
  //     "hash": "#qwert"
  // }
  return (
    <div>
      <ul>
        <li>News 1</li>
        <li>News 2</li>
        <li>News 3</li>
        <li>News 3</li>
      </ul>
    </div>
  );
}

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx

props.jsx