React 介绍
做什么
React 主要是用来操作 DOM 呈现页面(将数据渲染成 HTML 的开元 JavaScript 库)
谁开发
为什么学
- 原生 JS 操作 DOM 繁琐且效率低(DOM-API)
- 使用 JS 直接操作 DOM,浏览器会打大量的重绘重排
- 原生 JS 没有组件化编码方案,代码复用率很低
React 的特点
- 采用组件化模式,声明式代码,提高开发效率及组件复用率
- 在 React Native 中可以使用 React 语法进行移动端开发
- 使用虚拟 DOM+优秀的 diff 算法,尽量减少与真是 DOM 的交互
学习 React 之前要掌握的 JS 基础知识
- this
- class
- ES6
- npm
- 原型及原型链
- 数组常用方法
- 模块话
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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<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.返回值: POP、PUSH、REPLACE
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