目录
高阶组件HOC: Higher-Order Component,是一个函数,接收要包装的组件,返回增强后的组件
目的:实现状态逻辑复用
高阶组件就相当于手机壳, 采用包装(装饰)模式
1 创建一个函数,名称约定以with开头
2 指定函数的参数,参数以大写字母开头(作为要渲染的组件)
3 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
4 渲染参数组件,同时将状态通过props传递给参数组件
5 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- import React from "react";
- import ReactDOM from "react-dom";
-
- // 1 创建一个函数,名称约定以with开头
- // 2 指定函数的参数,参数以大写字母开头(作为要渲染的组件)
- function withMouse(WrappedComponent) {
-
- // 3 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
- class Mouse extends React.Component {
-
- //鼠标状态
- state = {
- x: 0,
- y: 0
- }
-
- //处理状态的逻辑代码
- handleMouseMove = e => {
- this.setState({
- x: e.clientX,
- y: e.clientY
- })
- }
-
- //监听鼠标移动事件
- componentDidMount() {
- window.addEventListener("mousemove", this.handleMouseMove)
- }
-
- //解绑事件
- componentWillUnmount() {
- window.removeEventListener('mousemove', this.handleMouseMove)
- }
-
- render() {
- //4 渲染参数组件,同时将状态通过props传递给参数组件
- return <WrappedComponent {...this.state}/>
-
- }
-
- }
- return Mouse;
- }
-
- const Position = props => (
- <p>
- 鼠标当前位置:(x:{props.x},y:{props.y})
- p>
- )
-
- //调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- const MousePosition = withMouse(Position)
-
- class App extends React.Component {
-
- render() {
- return (
- <div>
- {/*渲染增强后的组件*/}
- <MousePosition/>
- div>)
- }
- }
-
- ReactDOM.render(<App/>, document.getElementById("root"));
效果

加入鼠标随图片移动的功能
- import imgage from "./images/cat.jpg"
-
- const Cat = props => (
- <img src={imgage} alt='猫' style={{
- position: 'absolute',
- // 为了让鼠标在图片的中间,top减掉了图片的一半高度,left减掉了图片一半的宽度
- top: props.y - 180,
- left: props.x - 180
- }}/>
- )
-
-
- //调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- const MousePosition1 = withMouse(Cat)
效果

完整代码
- import React from "react";
- import ReactDOM from "react-dom";
- import imgage from "./images/cat.jpg"
-
- // 1 创建一个函数,名称约定以with开头
- // 2 指定函数的参数,参数以大写字母开头(作为要渲染的组件)
- function withMouse(WrappedComponent) {
-
- // 3 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
- class Mouse extends React.Component {
-
- //鼠标状态
- state = {
- x: 0,
- y: 0
- }
-
- //处理状态的逻辑代码
- handleMouseMove = e => {
- this.setState({
- x: e.clientX,
- y: e.clientY
- })
- }
-
- //监听鼠标移动事件
- componentDidMount() {
- window.addEventListener("mousemove", this.handleMouseMove)
- }
-
- //解绑事件
- componentWillUnmount() {
- window.removeEventListener('mousemove', this.handleMouseMove)
- }
-
- render() {
- //4 渲染参数组件,同时将状态通过props传递给参数组件
- return <WrappedComponent {...this.state}/>
-
- }
-
- }
- return Mouse;
- }
-
-
- const Cat = props => (
- <img src={imgage} alt='猫' style={{
- position: 'absolute',
- // 为了让鼠标在图片的中间,top减掉了图片的一半高度,left减掉了图片一半的宽度
- top: props.y - 125,
- left: props.x - 100
- }}/>
- )
-
-
- //调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- const MousePosition1 = withMouse(Cat)
-
- const Position = props => (
- <p>
- 鼠标当前位置:(x:{props.x},y:{props.y})
- p>
- )
-
- //调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- const MousePosition2 = withMouse(Position)
-
- class App extends React.Component {
-
- render() {
- return (
- <div>
- {/*渲染增强后的组件*/}
- <MousePosition1/>
- <MousePosition2/>
- div>)
- }
- }
-
- ReactDOM.render(<App/>, document.getElementById("root"));
默认情况下,react使用组件名称作为displayName
使用高阶组件存在的问题: 得到的两个组件名称相同

解决办法: 设置displayName → 用于调试时区分不同的组件

效果
完整代码
- import React from "react";
- import ReactDOM from "react-dom";
- import imgage from "./images/cat.jpg"
-
- // 1 创建一个函数,名称约定以with开头
- // 2 指定函数的参数,参数以大写字母开头(作为要渲染的组件)
- function withMouse(WrappedComponent) {
-
- // 3 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
- class Mouse extends React.Component {
-
- //鼠标状态
- state = {
- x: 0,
- y: 0
- }
-
- //处理状态的逻辑代码
- handleMouseMove = e => {
- this.setState({
- x: e.clientX,
- y: e.clientY
- })
- }
-
- //监听鼠标移动事件
- componentDidMount() {
- window.addEventListener("mousemove", this.handleMouseMove)
- }
-
- //解绑事件
- componentWillUnmount() {
- window.removeEventListener('mousemove', this.handleMouseMove)
- }
-
- render() {
- //4 渲染参数组件,同时将状态通过props传递给参数组件
- return <WrappedComponent {...this.state}/>
-
- }
-
- }
-
- //设置displayName
- Mouse.displayName = `withMouse${getDisplayName(WrappedComponent)}`
- return Mouse;
- }
- //displayName
- function getDisplayName(WrappedComponent) {
- return WrappedComponent.displayName || WrappedComponent.name || 'Component'
- }
-
- const Cat = props => (
- <img src={imgage} alt='猫' style={{
- position: 'absolute',
- // 为了让鼠标在图片的中间,top减掉了图片的一半高度,left减掉了图片一半的宽度
- top: props.y - 125,
- left: props.x - 100
- }}/>
- )
-
-
- //调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- const MousePosition1 = withMouse(Cat)
-
- const Position = props => (
- <p>
- 鼠标当前位置:(x:{props.x},y:{props.y})
- p>
- )
-
- //调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- const MousePosition2 = withMouse(Position)
-
- class App extends React.Component {
-
- render() {
- return (
- <div>
- {/*渲染增强后的组件*/}
- <MousePosition1/>
- <MousePosition2/>
- div>)
- }
- }
-
- ReactDOM.render(<App/>, document.getElementById("root"));
问题:
添加了一个属性, 打印了props

属性没显示在props里
![]()
原因:props传递丢失
解决办法:

效果
![]()
完整代码
- import React from "react";
- import ReactDOM from "react-dom";
-
-
- // 1 创建一个函数,名称约定以with开头
- // 2 指定函数的参数,参数以大写字母开头(作为要渲染的组件)
- function withMouse(WrappedComponent) {
-
- // 3 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
- class Mouse extends React.Component {
-
- //鼠标状态
- state = {
- x: 0,
- y: 0
- }
-
- //处理状态的逻辑代码
- handleMouseMove = e => {
- this.setState({
- x: e.clientX,
- y: e.clientY
- })
- }
-
- //监听鼠标移动事件
- componentDidMount() {
- window.addEventListener("mousemove", this.handleMouseMove)
- }
-
- //解绑事件
- componentWillUnmount() {
- window.removeEventListener('mousemove', this.handleMouseMove)
- }
-
- render() {
- //4 渲染参数组件,同时将状态通过props传递给参数组件
- return <WrappedComponent {...this.state}{...this.props}/>
-
- }
-
- }
-
- return Mouse;
- }
-
-
- const Position = props => {
- console.log(props)
- return (
- <p>
- 鼠标当前位置:(x:{props.x},y:{props.y})
- p>
- )
-
- }
-
- //调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中
- const MousePosition = withMouse(Position)
-
- class App extends React.Component {
-
- render() {
- return (
- <div>
- {/*渲染增强后的组件*/}
- <MousePosition a="1"/>
- div>)
- }
- }
-
- ReactDOM.render(<App/>, document.getElementById("root"));