8.context
8.1.事例
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 13:25:37
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
const PersonContext = React.createContext();
function getStyle(color) {
return {
border: `5px solid ${color}`,
padding: '5px',
marigin: '5px'
}
}
class Person extends React.Component {
state = {
color: 'red'
}
changeColor = (color) => this.setState({ color })
render() {
const value = { color: this.state.color, changeColor: this.changeColor }
return <PersonContext.Provider value={value}>
<div style={{ ...getStyle(this.state.color), width: '200px' }}>
Person
<Head />
<Body />
</div>
</PersonContext.Provider>
}
}
class Head extends React.Component {
static contextType = PersonContext;
render() {
return (
<div style={getStyle(this.context.color)}>
Head
<Eye />
</div>
)
}
}
class Body extends React.Component {
static contextType = PersonContext;
render() {
return (
<div style={getStyle(this.context.color)}>
Body
<Hand />
</div>
)
}
}
class Hand extends React.Component {
static contextType = PersonContext;
render() {
return (
<div style={getStyle(this.context.color)}>
Hand
<button onClick={() => this.context.changeColor('red')}>变红</button>
<button onClick={() => this.context.changeColor('green')}>变绿</button>
</div>
)
}
}
function Eye() {
return <PersonContext.Consumer>
{content => <div style={getStyle(content.color)}>Eye</div>}
</PersonContext.Consumer>
}
ReactDOM.render(<Person />, document.getElementById('root'));
8.2.实现
8.2.1.src/react.js
/*
* @Author: dfh
* @Date: 2021-02-24 18:34:24
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 13:33:21
* @Modified By: dfh
* @FilePath: /day25-react/src/react.js
*/
import Component from './Component';
import { wrapToVdom } from './utils';
/**
*
* @param {*} type 元素类型
* @param {*} config 配置对象
* @param {*} children 孩子或者孩子门
*/
function createElement(type, config, children) {
let ref, key;
if (config) {
delete config.__source;
delete config.__self;
ref = config.ref;
key = config.key;
delete config.ref;
delete config.key;
}
let props = { ...config };
if (arguments.length > 3) {//children是一个数组
props.children = Array.prototype.slice.call(arguments, 2).map(wrapToVdom);
} else {
props.children = wrapToVdom(children);
}
return {
type,
props,
ref,
key
}
}
function createRef() {
return { current: null }
}
+ function createContext(initialValue) {
+ Provider._value = initialValue;
+ function Provider(props) {
+ const { value } = props;
+ if (Provider._value) {
+ Object.assign(Provider._value, value)
+ } else {
+ Provider._value = value;
+ }
+ return props.children;
+ }
+ function Consumer(props) {
+ return props.children(Provider._value);
+ }
+ return { Provider, Consumer };
+ }
const React = {
createElement,
Component,
createRef,
+ createContext
}
export default React;
8.2.2.src/react-dom.js
/*
* @Author: dfh
* @Date: 2021-02-24 18:34:32
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 13:40:33
* @Modified By: dfh
* @FilePath: /day25-react/src/react-dom.js
*/
import { REACT_TEXT } from './constants';
import { addEvent } from './event';
/**
* 给跟容器挂载的时候
* @param {*} vdom 需要渲染的虚拟DOM
* @param {*} container 容器
*/
function render(vdom, container) {
const dom = createDOM(vdom);
//挂载真实DOM
container.appendChild(dom);
//调用生命周期方法componentDidMount
dom.componentDidMount && dom.componentDidMount();
}
/**
* 创建证实DOM
* @param {*} vdom 虚拟DOM
*/
export function createDOM(vdom) {
const {
type,
props,
key,
ref
} = vdom;
//创建真实DOM
let dom;
if (type === REACT_TEXT) {//是文本
dom = document.createTextNode(props.content);
} else if (typeof type === 'function') {//自定义函数组件
if (type.isReactComponent) {//类组件
return mountClassComponent(vdom);
} else {//函数组件
return mountFunctionComponent(vdom);
}
} else {//原生组件
dom = document.createElement(type);
}
//使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
updateProps(dom, {}, props);
if (typeof props.children === 'object' && props.children.type) {//只有一个儿子,并且是虚拟DOM
render(props.children, dom);//把儿子变成真实DOM,并且挂载到自己身上
} else if (Array.isArray(props.children)) {//有多个儿子
reconcileChildren(props.children, dom);
}
//将真实DOM挂载到虚拟DOM上,以便后面取
vdom.dom = dom;
//通过虚拟DOM创建真实DOM之后,虚拟DOM的ref属性的current属性等于真实DOM
ref && (ref.current = dom);
return dom;
}
/**
* 把一个类型为自定义类组件的虚拟DOM转化为一个真实DOM并返回
* @param {*} vdom 类型为自定义类组件的虚拟DOM
*/
function mountClassComponent(vdom) {
const { type: Clazz, props } = vdom;
//获取类的实例
const classInstance = new Clazz(props);
//context的实现
+ if (Clazz.contextType) {
+ classInstance.context = Clazz.contextType.Provider._value;
+ }
//让这个类组件的虚拟DOM的classInstance属性指向这个类组件的实例
vdom.classInstance = classInstance;
//调用生命周期方法componentWillMount
if (classInstance.componentWillMount) {
classInstance.componentWillMount();
}
//执行生命周期方法getDerivedStateFromProps
if (Clazz.getDerivedStateFromProps) {
const partialState = Clazz.getDerivedStateFromProps(classInstance.props, classInstance.state)
if (partialState) {
classInstance.state = { ...classInstance.state, ...partialState };
}
}
//获取虚拟DOM
const oldRenderVdom = classInstance.render();
//将虚拟DOM挂载的组件实例上,以便后面DOM-diff时用
classInstance.oldRenderVdom = vdom.oldRenderVdom = oldRenderVdom;
//获取真实DOM
const dom = createDOM(oldRenderVdom);
if (classInstance.componentDidMount) {
dom.componentDidMount = classInstance.componentDidMount;
}
//将真实dom挂到实例上上
classInstance.dom = dom;
return dom;
}
/**
* 把一个类型为自定义函数组件的虚拟DOM转换为一个真实DOM并返回
* @param {*} vdom 类型为自定义函数组件的虚拟DOM
*/
function mountFunctionComponent(vdom) {
const { type: FunctionComponent, props } = vdom;
const renderVdom = FunctionComponent(props);
vdom.oldRenderVdom = renderVdom;
return createDOM(renderVdom);
}
/**
*
* @param {*} childrenVdom 孩子门的虚拟DOM
* @param {*} parentDOM 要挂载到的真实DOM
*/
function reconcileChildren(childrenVdom, parentDOM) {
for (let i = 0; i < childrenVdom.length; i++) {
const child = childrenVdom[i];
render(child, parentDOM);//把儿子挂载的自己身上
}
}
/**
* 使用虚拟DOM的属性更新刚创建出来的真实DOM的属性
* @param {*} dom 真实DOM
* @param {*} props 虚拟DOM属性
*/
function updateProps(dom, oldProps, props) {
for (const key in props) {
if (key === 'children') continue;//单独处理,不再此处处理
if (key === 'style') {
const styleObj = props.style;
for (const attr in styleObj) {
dom.style[attr] = styleObj[attr];
}
} else if (key.startsWith('on')) {//onClick=>onclick
// dom[key.toLocaleLowerCase()]=props[key];
addEvent(dom, key.toLocaleLowerCase(), props[key]);
} else {//在JS中定义class使用的是className,所以不要改
dom[key] = props[key];
}
}
}
/**
* 对当前组件进行DOM-DIFF
* @param {*} parentDOM 老得父真实DOM
* @param {*} oldRenderVdom 老得虚拟DOM
* @param {*} newRenderVdom 新的虚拟DOM
* @param {*} nextDom 下一个真实DOM,主要用来插入找位置用
*/
export function compareTwoVdom(parentDOM, oldRenderVdom, newRenderVdom, nextDom) {
if (!oldRenderVdom && !newRenderVdom) {//新老虚拟DOM都为null
return null;
} else if (oldRenderVdom && !newRenderVdom) {//新的虚拟DOM为NULL,老得存在
const currentDOM = findDOM(oldRenderVdom);//找到此虚拟DOM对应的真实DOM
currentDOM && parentDOM.removeChild(currentDOM);//移除此老得真实DOM
//调用生命周期方法
oldRenderVdom.classInstance && oldRenderVdom.classInstance.componentWillUnmount && oldRenderVdom.classInstance.componentWillUnmount()
} else if (!oldRenderVdom && newRenderVdom) {//新的虚拟DOM存在,老得虚拟DOM为NULL
const newDOM = createDOM(newRenderVdom);//获取真实DOM
if (nextDom) {
parentDOM.insertBefore(newDOM, nextDom);
} else {
parentDOM.appendChild(newDOM);
}
} else if (oldRenderVdom && newRenderVdom && oldRenderVdom.type !== newRenderVdom.type) {//新老虚拟DOM都存在,但是类型不同
const oldDOM = findDOM(oldRenderVdom);//老得真实DOM
const newDOM = createDOM(newRenderVdom);//新的真实DOM
parentDOM.replaceChild(newDOM, oldDOM);
//调用生命周期方法
oldRenderVdom.classInstance && oldRenderVdom.classInstance.componentWillUnmount && oldRenderVdom.classInstance.componentWillUnmount()
} else {//新老都有,类型也一样,要进行深度DOM-DIFF
updateElement(oldRenderVdom, newRenderVdom);
}
}
/**
* 深度对比两个虚拟DOM
* @param {*} oldRenderVdom 老得虚拟DOM
* @param {*} newRenderVdom 新的虚拟DOM
*/
function updateElement(oldRenderVdom, newRenderVdom) {
if (oldRenderVdom.type === REACT_TEXT) {//文本
const currentDOM = newRenderVdom.dom = oldRenderVdom.dom;//复用老得真实DOM节点
currentDOM.textContent = newRenderVdom.props.content;//直接修改老的DOM节点的文件就可以了
} else if (typeof oldRenderVdom.type === 'string') {//说明是一个原生组件
const currentDOM = newRenderVdom.dom = oldRenderVdom.dom;//复用老得真实DOM
//先更新属性
updateProps(currentDOM, oldRenderVdom.props, newRenderVdom.props);
//比较儿子们
updateChildren(currentDOM, oldRenderVdom.props.children, newRenderVdom.props.children);
} else if (typeof oldRenderVdom.type === 'function') {
if (oldRenderVdom.type.isReactComponent) {
updateClassComponent(oldRenderVdom, newRenderVdom);//老新都是类组件,进行类组件更新
} else {
updateFunctionComponent(oldRenderVdom, newRenderVdom);//新老都是函数组件,进行函数组件更新
}
}
}
/**
* 如果老得虚拟DOM节点和新的虚拟DOM节点都是函数的话,走这个更新逻辑
* @param {*} oldVdom 老得虚拟DOM
* @param {*} newVdom 新的虚拟DOM
*/
function updateFunctionComponent(oldVdom, newVdom) {
const parentDOM = findDOM(oldVdom).parentNode;//找到老得父节点
const { type: FunctionComponent, props } = newVdom;
const oldRenderVdom = oldVdom.oldRenderVdom;//老得的渲染虚拟DOM
const newRenderVdom = FunctionComponent(props);//新的渲染虚拟DOM
compareTwoVdom(parentDOM, oldRenderVdom, newRenderVdom);//比较虚拟DOM
newVdom.oldRenderVdom = newRenderVdom;
}
/**
* 如果老得虚拟DOM节点和新的虚拟DOM节点都是类组件的话,走这个更新逻辑
* @param {*} oldVdom 老得虚拟DOM
* @param {*} newVdom 新的虚拟DOM
*/
function updateClassComponent(oldVdom, newVdom) {
const classInstance = newVdom.classInstance = oldVdom.classInstance;//复用老得类的实例
newVdom.oldRenderVdom = oldVdom.oldRenderVdom;//上一次类组件的渲染出来的虚拟DOM
if (classInstance.componentWillReceiveProps) {//组件将要接受到新的属性
classInstance.componentWillReceiveProps();
}
//触发组件的更新,把新的属性传递过去
classInstance.updater.emitUpdate(newVdom.props);
}
/**
* 深度比较孩子们
* @param {*} parentDOM 父DOM
* @param {*} oldChildren 老得儿子们
* @param {*} newChildren 新的儿子们
*/
function updateChildren(parentDOM, oldChildren, newChildren) {
//孩子可能是数组或者对象(单节点是对象)
oldChildren = Array.isArray(oldChildren) ? oldChildren : [oldChildren];
newChildren = Array.isArray(newChildren) ? newChildren : [newChildren];
//获取最大的长度
const maxLen = Math.max(oldChildren.length, newChildren.length);
for (let i = 0; i < maxLen; i++) {
//在儿子们里查找,找到索引大于当前索引的
const nextDOM = oldChildren.find((item, index) => index > i && item && item.dom)
//递归比较孩子
compareTwoVdom(parentDOM, oldChildren[i], newChildren[i], nextDOM && nextDOM.dom);
}
}
/**
* 查找此虚拟DOM对象的真实DOM
* @param {*} vdom
*/
export function findDOM(vdom) {
const { type } = vdom;
let dom;
if (typeof type === 'function') {
dom = findDOM(vdom.oldRenderVdom)
} else {
dom = vdom.dom;
}
return dom
}
const ReactDOM = {
render
}
export default ReactDOM;
9.高阶组件
9.1.配置
- 安装
npm install react-app-rewired customize-cra @babel/plugin-proposal-decorators- D
package.json
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test",
"eject": "react-app-rewired eject"
}
config-overrides.js
const {override,addBabelPlugin}=require('customize-cra');
module.exports=override(
addBabelPlugin([
'@babel/plugin-proposal-decorators',{'legacy':true}
])
)
jsconfig.json
{
'compilerOptions':{
'experimentalDecorators':true
}
}
9.2.按理
9.2.1.属性代理
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 15:25:38
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
/**
* 高阶组件 有三中应用场景
* 1.属性代理
*/
const widthLoading = msg => OldComponent => {
return class extends React.Component {
show = () => {
const div = document.createElement('div')
div.setAttribute('id', 'load');
div.innerHTML = `
<p style="position:absolute;top:50%;left:50%;z-index:10;background-color:gray">${msg}</p>
`
document.body.appendChild(div);
}
hide = () => {
document.getElementById('load').remove();
}
render() {
return <OldComponent show={this.show} hide={this.hide} />
}
}
}
@widthLoading('正在加载中...')
class Hello extends React.Component {
render() {
return <div>
<p>hello</p>
<button onClick={this.props.show}>显示</button>
<button onClick={this.props.hide}>隐藏</button>
</div>
}
}
ReactDOM.render(<Hello />, document.getElementById('root'));
9.2.2.反向继承
基于反向方向继承可以对原有组件进行扩展
src/index.js
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 17:21:34
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from './react';
import ReactDOM from './react-dom';
/**
* 高阶组件 有三中应用场景
* 1.属性代理
* 2.反向继承
*/
class Button extends React.Component {
state = { name: '张三' }
componentWillMount() {
console.log('button componentWillMount');
}
componentDidMount() {
console.log('button componentDidMount')
}
render() {
return (<button name={this.state.name} title={this.props.title}></button>)
}
}
const wrapper = Button => {
return class extends Button {
state = { number: 0 }
componentWillMount() {
console.log('WrapperButton componentWillMount');
}
componentDidMount() {
console.log('WrapperButton componentDidMount');
}
handlerClick = () => {
this.setState({ number: this.state.number + 1 });
}
render() {
const renderElement = super.render();
const newProps = {
...renderElement.props,
...this.state,
onClick: this.handlerClick
}
return React.cloneElement(renderElement, newProps, this.state.number);
}
}
}
const WrapperButton = wrapper(Button)
ReactDOM.render(<WrapperButton title='标题' />, document.getElementById('root'));
src/react.js
/*
* @Author: dfh
* @Date: 2021-02-24 18:34:24
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 17:21:21
* @Modified By: dfh
* @FilePath: /day25-react/src/react.js
*/
import Component from './Component';
import { wrapToVdom } from './utils';
/**
*
* @param {*} type 元素类型
* @param {*} config 配置对象
* @param {*} children 孩子或者孩子门
*/
function createElement(type, config, children) {
let ref, key;
if (config) {
delete config.__source;
delete config.__self;
ref = config.ref;
key = config.key;
delete config.ref;
delete config.key;
}
let props = { ...config };
if (arguments.length > 3) {//children是一个数组
props.children = Array.prototype.slice.call(arguments, 2).map(wrapToVdom);
} else {
props.children = wrapToVdom(children);
}
return {
type,
props,
ref,
key
}
}
function createRef() {
return { current: null }
}
function createContext(initialValue) {
Provider._value = initialValue;
function Provider(props) {
const { value } = props;
if (Provider._value) {
Object.assign(Provider._value, value)
} else {
Provider._value = value;
}
return props.children;
}
function Consumer(props) {
return props.children(Provider._value);
}
return { Provider, Consumer };
}
+ function cloneElement(oldElement, newProps, ...newChildren) {
+ let children = oldElement.props.children;
+ //children可能是undefined,对象,数组
+ if (children) {
+ if (!Array.isArray(children)) {//是一个对象
+ children = [children]
+ }
+ } else {//undefined
+ children = [];
+ }
+ children.push(...newChildren);
+ children = children.map(wrapToVdom);
+ if (children.length === 0) {
+ children = undefined;
+ } else if (children.length === 1) {
+ children = children[0];
+ }
+ newProps.children = children;
+ const props = { ...oldElement.props, ...newProps };
+ return { ...oldElement, props };
+ }
const React = {
createElement,
Component,
createRef,
createContext,
+ cloneElement
}
export default React;
10.render props
render props是值一种在react组件之间使用一个值为函数的props共享代码的简单技术- 具有
render props组件接受一个函数,该函数返回一个react元素并调用它而不是实现自己的渲染逻辑 render props是一个用于告知组件需要渲染什么内容的函数props
10.1.原生实现
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 19:06:45
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handeMouseMove = event => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return <div onMouseMove={this.handeMouseMove}>
<h1>移动鼠标</h1>
<p>当前的鼠标位置是:({this.state.x},{this.state.y})</p>
</div>
}
}
ReactDOM.render(<MouseTracker />, document.getElementById('root'));
10.2.render属性
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 19:17:45
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
/**
* render props
* 1.render props在React组件之间使用一个值为函数的props共享代码
*
*/
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handeMouseMove = event => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return <div onMouseMove={this.handeMouseMove}>
{this.props.render(this.state)}
</div>
}
}
ReactDOM.render(<MouseTracker render={props => <>
<h1>移动鼠标</h1>
<p>当前的鼠标位置是:({props.x},{props.y})</p>
</>} />, document.getElementById('root'));
10.3.children
children是一个渲染方法
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 19:21:16
* @Modified By: dfh
* @FilePath: /day25-react/src/index-render-props1.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handeMouseMove = event => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return <div onMouseMove={this.handeMouseMove}>
{this.props.children(this.state)}
</div>
}
}
ReactDOM.render(<MouseTracker >{
props => <>
<h1>移动鼠标</h1>
<p>当前的鼠标位置是:({props.x},{props.y})</p>
</>
}</MouseTracker>, document.getElementById('root'));
10.4.hoc
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 19:35:21
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
function widthTracker(OldComponent) {
return class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handeMouseMove = event => {
this.setState({
x: event.clientX,
y: event.clientY
})
}
render() {
return <div onMouseMove={this.handeMouseMove}>
<OldComponent {...this.state} />
</div>
}
}
}
function Show(props) {
return <>
<h1>移动鼠标</h1>
<p>当前的鼠标位置是:({props.x},{props.y})</p>
</>
}
const MouseTracker = widthTracker(Show);
ReactDOM.render(<MouseTracker />, document.getElementById('root'));
11.shouldComponentUpdate
- 当一个组件的
props或state变更,React会将最新返回的元素与之前渲染的元素进行对比,以此决定是否有必要更新真实的DOM,当它们不相同时React会更新改DOM - 如果渲染的组件非常多时,可以通过覆盖生命周期方法
shouldComponentUpdate来进行优化 shouldComponentUpdate方法会在重新渲染前触发。其默认实现是返回true。如果组件不需要更新,可以在shouldComponentUpdate中返回false来跳过整个渲染过程。其包括该组件的render调用以及之后的操作。
11.1.事例-Component
存在的问题:无论点击Number1中的button,还是Number2中的butter,所有的render都会执行
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 19:41:39
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
/**
* 默认情况下,只要状态改变,那么所有的组件不管属性有没有变化都会更新
*/
class Counter extends React.Component {
state = { num1: 0, num2: 0 }
addNum1 = () => {
this.setState({ num1: this.state.num1 + 1 })
}
addNum2 = () => {
this.setState({ num2: this.state.num2 + 1 })
}
render() {
console.log('render Counter')
return <div>
<Number1 num={this.state.num1} add={this.addNum1} />
<Number2 num={this.state.num2} add={this.addNum2} />
</div>
}
}
class Number1 extends React.Component {
render() {
console.log('render Number1')
return <div>
<button onClick={this.props.add}>Number1:{this.props.num}</button>
</div>
}
}
class Number2 extends React.Component {
render() {
console.log('render Number2')
return <div>
<button onClick={this.props.add}>Number2:{this.props.num}</button>
</div>
}
}
ReactDOM.render(<Counter />, document.getElementById('root'));
11.2.事例-PuerComponent
当点击Number1中的按钮时,只走
render Counter和render Number1,但点击Number2中的按钮时只走render Counter和render Number2
/*
* @Author: dfh
* @Date: 2021-02-24 18:18:22
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 19:46:48
* @Modified By: dfh
* @FilePath: /day25-react/src/index.js
*/
import React from 'react';
import ReactDOM from 'react-dom';
class Counter extends React.Component {
state = { num1: 0, num2: 0 }
addNum1 = () => {
this.setState({ num1: this.state.num1 + 1 })
}
addNum2 = () => {
this.setState({ num2: this.state.num2 + 1 })
}
render() {
console.log('render Counter')
return <div>
<Number1 num={this.state.num1} add={this.addNum1} />
<Number2 num={this.state.num2} add={this.addNum2} />
</div>
}
}
class Number1 extends React.PureComponent {
render() {
console.log('render Number1')
return <div>
<button onClick={this.props.add}>Number1:{this.props.num}</button>
</div>
}
}
class Number2 extends React.PureComponent {
render() {
console.log('render Number2')
return <div>
<button onClick={this.props.add}>Number2:{this.props.num}</button>
</div>
}
}
ReactDOM.render(<Counter />, document.getElementById('root'));
11.3.PuerComponent实现
/*
* @Author: dfh
* @Date: 2021-03-01 19:49:40
* @LastEditors: dfh
* @LastEditTime: 2021-03-01 22:28:52
* @Modified By: dfh
* @FilePath: /day25-react/src/PureComponent.js
*/
import Component from './Component';
class PureComponent extends Component {
shouldComponentUpdate(nextProps, nextState) {
const value=!shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState)
console.log(value)
return value;
}
}
/**
* 用浅比较比较obj1和obj2是否相等
* 只要内存地址一样,就认为相等,不一样就不相等
* @param {*} obj1
* @param {*} obj2
*/
function shallowEqual(obj1, obj2) {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) return false;
const key1 = Object.keys(obj1);
const key2 = Object.keys(obj2);
if (key1.length !== key2.length) return false;
for (let key of key1) {
if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) return false;
}
return true;
}
export default PureComponent;