React 旧生命周期
Initialization
- 初始化阶段(Initialization): 没有生命周期函数,只是在constructor里做数据的设置
Mounting
只有页面在第一次渲染的时候才会执行Mounting过程
UNSAFE_componentWillMount组件挂载前
有this 没dom 可以不通过setState来修改数据
因为这个生命周期在render函数之前,所以就不用通过setState修改数据
可以做网路请求 能不用就不用都快被干掉了
render函数:挂载
注意父组件的render执行 子组件的render 必定执行
componentDidMount函数:挂载完毕
有this 有dom 可以修改数据 通过setState
dom的初始化操作 网络请求
子组件的优先挂载完成 之后才是父组件
Updation(更新): 数据发生变化时执行
里面分别有两个一个是props更新的时候触发
一个是自己states更新的时候触发
props更新的时候
UNSAFE_componentWillReceiveProps
可以在子组件的render函数执行前获取新的props,从而更新子组件自己的state。
这样的好处是,可以将数据请求放在这里进行执行,需要传的参数则从componentWillReceiveProps(nextProps)中获取。而不必将所有的请求都放在父组件中。于是该请求只会在该组件渲染时才会发出,从而减轻请求负担。
实现监听props改变 重新发起请求 修改state值,不需要setState,因为后面有render函数
里面第一个参数是改变后的props
这个生命周期要注意:数据改变的来源尽量只来自于1方 ,例如当通过props修改子组件的state.age,有通过子组件自身修改state.age会相互覆盖的
UNSAFE_componentWillReceiveProps(props) {
// 实现监听props改变 重新发起请求 修改state值
console.log("props数据发生改变");
console.log(props);
// this.state.name = props.name;
// 修改数据不需要执行state
// 注意数据改变的来源尽量只来自于1方
}
shouldComponentUpdate
性能优化的声明周期 可以控制结接下来的render是否执行 默认是ture
参数是修改后的prop 和 state
shouldComponentUpdate(props, state) {
return state.name !== this.state.name;
} //这样的话只有在state.name改变的时候才会渲染
//但是有个问题,当监听多个state值是否更新怎么写
当监听多个state值是否更新怎么写 (不推荐JSON.stringify存在效率问题)
shouldComponentUpdate(props, state) {
return JSON.stringify(this.state) !== JSON.stringify(state);
} //不推荐JSON.stringify存在效率问题
PureComponent 组件 自带shouldComponentUpdate
在用的时候
import React, { PureComponent} from "react";
class App extends PureComponent{
}
componentWillUpdate() 接下来是组件更新前
this有 数据应该是新的 dom是老的 不能做数据修改的操作
UNSAFE_componentWillUpdate() {
console.log("父组件更新前");
console.log(this.state.age);//会变
console.log(this.ageRef.current.innerHTML)//不会变
}
再执行render渲染函数
componentDidUpdate()组件更新结束
不能做修改数据的操作 死循环 -> 可以通过shouldComponentUpdate 挽救一下
componentDidUpdate() {
console.log("父组件更新结束");
console.log(this.state.age) //改了
console.log(this.ageRef.current.innerHTML)//改了
}
state与props一样,就是少了 UNSAFE_componentWillReceiveProps
componentWillUnmount 组件卸载
指在某个组件不在显示的时候触发。就很vue里面的v-if=false的时候触发
新的生命周期
static getDerivedStateFromProps
这个生命周期函数是为了替代componentWillReceiveProps存在的,所以在你需要使用componentWillReceiveProps的时候,就可以考虑使用getDerivedStateFromProps来进行替代了然后就进行render进行
static getDerivedStateFromProps(nextProps, nextState)两个参数,第一个参数是传过来的props,第二参数是现在里面的属性
class Son1 extends Component {
constructor() {
super();
}
state = {
age:2,
name:'li'
}
static getDerivedStateFromProps(nextProps, nextState) {
console.log("getDerivedStateFromProps")
console.log(this)
console.log(nextProps,nextState)
// 静态方法不能获取this
// 参数是新的props,state
// 需要return 数据放入到state 将props 映射到state里
// 需要根据不同的情况去return 不同值
// 将props来源的数据映射到state
const propsName = nextProps.name
const stateName = nextState.name
if(stateName) { //这个判断是子组件的state要是有name就不能通过props传递过来的值进行改变
return null
} else {
return { name: propsName }
}
}
componentDidMount() 组件挂载后
getSnapshotBeforeUpdate()组件更新前快照
三个参数:
prevProps 组件更新前的props, prevState,组件更新前的State shap?
更新之前记录数据,返回给componentDidUpdate
必须和componentDidUpdate成对出现
componentDidUpdate(props,state,shap) 更新结束
三个参数,分别是getSnapshotBeforeUpdate传递过来的更新前的快照
用法:
getSnapshotBeforeUpdate(prevProps,prevState, shap) {
console.log("组件更新前快照")
console.log(prevProps,prevState,shap)
// 更新之前记录数据,返回给componentDidUpdate
// 必须和componentDidUpdate成对出现
return {
name:123
}
}
componentDidUpdate(props,state,shap) {
// 接受getSnapshotBeforeUpdate 保存的快照信息
console.log("更新结束")
console.log(props,state,shap)
}
}
context上下文
通过context进行通信
1. 创建context对象
2. 对上线文对象处理,目的是提供者和消费者配对
3. 通过provider value 属性提供数据
4. 通过consumer消费者获取数据
5. context上下文的数据直接修改无效,在传递value的同时传递一个修改上下文的方法
用法:在函数组件中可以简化,通过hooks里面的useContext就能直接获取
用法1
首先创建一个nameContext.js文件夹里面是用来引入createContext,创建文件夹是为了区分多个上下文的
import { createContext } from "react";
const NameContext = createContext();
const { Consumer, Provider } = NameContext;
export default NameContext;
export { Consumer, Provider };
在孙子组件里面Grandson2.js <></>包裹因为在render consumer的时候外层会包裹一层div
import React, { PureComponent, createContext } from "react";
import { Consumer } from "./nameContext"
import ColorContext from "./colorContext";
class GrandSon2 extends PureComponent {
constructor() {
super();
}
state = {}
render() {
console.log(this)
return (
<>
{/* 第一个上下文 */}
<Consumer> //通过组件来呈现视图
{
(context) => //content是该组件的上下文,里面有index爷爷组件传递过来的name,与changeName参数,就可以直接使用了
return (
<div className="grandson2">
<h2>孙子组件2</h2>
{context.name}
<button onClick={() => {
context.changeName("王心凌")
}}>改context名字</button>
</div>
)
}
}
</Consumer>
{/* color上下文 */}
</>
)
}
componentDidMount() {
console.log("grandson2", this)
}
}
/*
1. consumer 内部是可以获取上下文的
2. consumer 内部有一个渲染函数
this.props.chilren()
3. 将上下文作为渲染函数的参数
4. 渲染函数指的就是Consumer 组件标签中间的函数
*/
export default GrandSon2;
在son2.js里面
import React, { PureComponent } from "react";
import GrandSon2 from "./GrandSon2";
class Son2 extends PureComponent {
constructor() {
super();
}
state = {}
render() {
return (
<div>
<h2>子组件2</h2>
<GrandSon2>
</GrandSon2>
</div>);
}
}
export default Son2;
在爷爷组件里面
import React, { PureComponent } from "react";
import ColorContext from "./colorContext";
import { Provider } from "./nameContext";
import Son1 from "./Son1";
import Son2 from "./Son2";
class Demo extends PureComponent {
constructor() {
super();
}
state = {
name: "韩梅梅",
};
changeName = (name) => {
this.setState({ name });
};
render() {
const { name } = this.state;
const { changeName } = this;
return (
<Provider value={{ name,changeName }}>//通过Provider把name,与changeName 属性传给孙子,孙子接收到就可以使用了
<div>
<h1>contextDemo</h1>
<Son1></Son1>
<Son2></Son2>
</div>
</Provider>
);
}
}
export default Demo;
方法2 通过静态方法static
static contextType = NameContext; //通过这个方法可以直接拿到context,不用Consumer
还是使用刚开始定义的nameContext.js
然后是son1父组件son1.js 与son2.js是一样的不一样的是在接收content的那个组件
son1.js
import React, { PureComponent } from "react";
import ColorContext from "./colorContext";
import NameContext from "./nameContext";
import GrandSon1 from "./GrandSon1";
class Son1 extends PureComponent {
// 静态属性只能赋值一个上下文 多个还是需要通过consumer实现
constructor() {
super();
}
state = {}
render() {
return (
<div>
<h2>子组件1</h2>
<GrandSon1></GrandSon1>
</div>);
}
componentDidMount() {
console.log("son1",this)
}
}
export default Son1;
Grandson1.js 通过静态方法拿到contxt
import React, { PureComponent } from "react";
import colorContext from "./nameContext";
class GrandSon1 extends PureComponent {
static contextType = colorContext;
// 将上下文对象映射到组件内部的context
constructor() {
super();
}
state = {}
render() {
return (
<div>
<h3>孙子组件1</h3>
{this.context.name}
</div>);
}
componentDidMount() {
console.log("grandSon1",this)
setTimeout(() => {
this.context.changeName("王心凌")
},2000)
// console.log(this.context.name)
// this.setState({})
// this.forceUpdate();
}
}
export default GrandSon1;
如果有多个上下文
注意这里我们只能用第一种方法通过consumer实现,不能通过静态方法static实现
实现方法:
我们在创建一个colorContext.js
import { createContext } from "react"
const ColorContext = createContext()
export default ColorContext
在son2里
import React, { PureComponent } from "react";
import GrandSon2 from "./GrandSon2";
class Son2 extends PureComponent {
constructor() {
super();
}
state = {}
render() {
return (
<div>
<h2>子组件2</h2>
<GrandSon2>
</GrandSon2>
</div>);
}
}
export default Son2;
在Grandson2.js里面 ,通过<ColorContext.Consumer>来区分是哪一个上线文
import React, { PureComponent, createContext } from "react";
import { Consumer } from "./nameContext"
import ColorContext from "./colorContext";
class GrandSon2 extends PureComponent {
constructor() {
super();
}
state = {}
render() {
console.log(this)
return (
<>
{/* 第一个上下文 */}
<Consumer>
{
(context) => {
console.log("nameContext",context)
return (
<div className="grandson2">
<h2>孙子组件2</h2>
{context.name}
<button onClick={() => {
context.changeName("王心凌")
}}>改context名字</button>
</div>
)
}
}
</Consumer>
{/* color上下文 */}
<ColorContext.Consumer>
{(context) => {
console.log("ColorContext.Consumer",context)
return <div>{context.color}</div>
}}
</ColorContext.Consumer>
</>
)
}
componentDidMount() {
console.log("grandson2", this)
}
}
export default GrandSon2;
在index.js爷爷组件里面
import React, { PureComponent } from "react";
import ColorContext from "./colorContext";
import { Provider } from "./nameContext";
console.log(ColorContext);
// console.log("context", context)
import Son1 from "./Son1";
import Son2 from "./Son2";
class Demo extends PureComponent {
constructor() {
super();
}
state = {
name: "韩梅梅",
};
changeName = (name) => {
this.setState({ name });
};
render() {
const { name } = this.state;
const { changeName } = this;
return (
<Provider value={{ name ,changeName}}> //通过Provider 包裹的形式,谁包裹谁无所谓
<ColorContext.Provider value={{ color: 'red' }}>
<div>
<h1>contextDemo</h1>
<Son1></Son1>
<Son2></Son2>
</div>
</ColorContext.Provider>
</Provider>
);
}
componentDidMount() {}
}
export default Demo;
当直接传递一个默认值的时候
colorContext.js
import { createContext } from "react";
export default createContext({ color: "red" });
son1.js
直接就可以通过默认值就可以传递过来
import colorContext from "./colorContext";
import sizeContext from "./sizeContext";
function Son1() {
return (
<div>
<colorContext.Consumer>
{(color) => {
console.log(color);
return (
<sizeContext.Consumer>
{(size) => {
console.log(size, color);
return (
<p style={{ color: color.color, fontSize: size.size + "px" }}>
三个金
</p>
);
}}
</sizeContext.Consumer>
);
}}
</colorContext.Consumer>
</div>
);
}
export default Son1;
index.js
知识点,不用加通过provider进行传值,直接使用默认值就可以
import Son1 from "./Son1";
import Son2 from "./Son2"
function Index() {
return (
<div>
<Son1></Son1>
<hr />
<Son2></Son2>
</div>
);
}
portal
首先来判断一句话的真假,所有组件都是挂载在根组件上,这句话没错,但是说所有组件的dom都是挂载到根组件的dom上,这就话就错了,我们可以通过portal这个属性来修改组件挂载的位置,例如做个
这样的样式,我们可以是根组件的孙子组件,但是我们可以把dom挂载在与根组件同级的效果上,这样显示的时候我们就不用z-index:99999了,但是组件的先后顺序并没变,所以我们点击这个图的任意位置,依然会冒泡到他的爷爷组件
接下来我们看下这个怎么用
首先先创建一个Model.js 是用来实现遮罩层这个组件的
知识点:
要引入import ReactDOM from "react-dom";
要创建两个类最后一个类用第一个类return ReactDOM.createPortal(<Modal close={this.props.close}></Modal>,document.body) 后面的document.body就是挂载到哪里
import React, { PureComponent } from "react";
import ReactDOM from "react-dom";
class Modal extends PureComponent {
constructor() {
super();
}
state = {};
render() {
return (
<div
className="model"
style={{
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
background: "rgba(0,0,0,.6)",
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
loading
<button onClick={this.props.close}>关闭</button>
</div>
);
}
componentDidMount() {}
}
class Box extends PureComponent {
render() {
return ReactDOM.createPortal(<Modal close={this.props.close}></Modal>,document.body)
}
}
export default Box;
在index.js里面
import React, { PureComponent } from "react";
import Modal from "./Modal";
class Demo extends PureComponent {
constructor() {
super();
}
state = {
show: false,
};
close = () => {
this.setState({ show: false });
};
render() {
return (
<div className="testDemo">
<h1>portal</h1>
<button
onClick={() => {
this.setState({ show: true });
}}
>
open
</button>
<div
onClick={() => {
this.close();
}}
>
{this.state.show && <Modal close={this.close}></Modal>}
</div>
</div>
);
}
componentDidMount() {}
}
export default Demo;
高阶组件
hoc 就是一个函数
接受一个组件作为参数
返回一个新的组件
在返回的新组件中渲染参数组件
将多个组件中的通用能力进行抽离,抽离为组件进行逻辑处理
处理结束将结果通过props 传递给抽离前的组件
hoc特点 参数都是从props中获取的
例如:在son1里面有一部分逻辑与son2的逻辑一模一样,我们就可以把这一部分的逻辑放入到高阶组件里面,然后导出这个组件的时候就把这个组件传到高阶组件里面,导出去,在父组件里面使用
例子:在index组件state里面有个name,把这个name存到localstorng里面然后son1组件拿到这个localstorng的name,赋值给son1组件里面的props.name,同样son2也是这样的操作,所以我们可以把son1与son2组件里面拿到这个localstorng的name,赋值给son1,son2组件里面的props.name,这个逻辑可以写到高阶组件里面
代码展示:
首先是hoc.js
知识点: <Fragment> 因为会多一层div,所以用Fragment来代替
<Fragment>
<ContainConponet {...result} ></ContainConponet>
{/* <ContainConponet name={result.name}></ContainConponet> */}
{/* 注意在props传值得时候可以直接传递一个对象不用是一个自定义属性={}
result是{name: '韩梅梅', age: 123}*/}
</Fragment>);
import React, { Component, Fragment } from "react";
export default (ParamComponent) => {//这个地方的ParamComponent就是传递进来的组件
class NewComponent extends Component {
constructor() {
super();
}
state = {
name: ''
}
render() {
return (
<Fragment> //因为会多一层div,所以用Fragment来代替
<ParamComponent name={this.state.name}></ParamComponent>
//这里通过自定义属性传到组件的props里面
</Fragment>);
}
componentDidMount() { //这个地方就是son1,son2 里面公共有的方法
let name = localStorage.getItem("name");
this.setState({name})
}
}
return NewComponent;
}
son1.js
import React, { PureComponent } from "react";
import hoc from "./hoc";
class Hello extends PureComponent {
constructor(props) {
super(props);
}
state = {
name: '',
}
render() {
return (
<div>
sayhello:{ this.props.name}
</div>);
}
// componentDidMount() { //这里是之前son1,与son2共有的逻辑,所以就不写在组件内部,都写在高阶组件hoc里面了
// let name = localStorage.getItem("name");
// this.setState({name})
// }
}
export default hoc(Hello);//把Hello这个组件当作参数放到hoc高阶函数里面返回一个新的组件
son2.js
知识点:可用修饰器
import React, { PureComponent } from "react";
import Hoc from "./hoc";
@Hoc //这个地方用到了装饰器,就相当于把Byby放到Hoc 的参数里面
class Byby extends PureComponent {
constructor() {
super();
}
state = {
name: '',
}
render() {
console.log("byby",this)
return (
<div>
sayByBy:{ this.props.name}
</div>);
}
// componentDidMount() {
// let name = localStorage.getItem("name");
// this.setState({name})
// }
}
export default Byby
在父组件进行挂载的时候,就是之前正常的挂载,变的只是son1,son2组件,并且多了个Hot高阶组件
index.js
import React, { PureComponent } from "react";
import Hello from "./hello";
import Byby from "./byby";
class Demo extends PureComponent {
constructor() {
super();
}
state = {}
render() {
return (
<div>
<h1>高阶组件hoc</h1>
<Byby></Byby>
<Hello></Hello>
</div>);
}
componentDidMount() {
localStorage.setItem("name","韩梅梅")
}
}
export default Demo;