react高阶组件

一. 定义

  • 官方定义:参数为组件,返回值为新组件的函数

  • 本质:是函数而非组件,是对原有组件进行拦截封装的新组件,本质上是一种设计模式而非React API

md 复制代码
 -   特点:
    -   接收一个组件作为参数
    -   返回一个新组件
    -   对新组件进行拦截和增强
  • 调用方式:const EnhancedComponent = higherOrderComponent(WrappedComponent)

  • 常见应用:

    • Redux中的connect函数(返回高阶组件)
    • React Router中的withRouter函数
  • 实现原理:

    • 结构:接收一个组件作为参数,返回一个新的增强组件
    • 命名规范:可通过displayName属性修改组件调试名称
    • 继承方式:新组件通常继承自PureComponent以获得性能优化

基础示例

jsx 复制代码
import React, { PureComponent } from 'react'

// 定义一个高阶组件
function hoc(Cpn) {
  // 1.定义类组件
  class NewCpn extends PureComponent {
    render() {
      return <Cpn name="why"/>
    }
  }
  // 设置 displayName动态命名
  NewCpn.displayName = `HOC(${Cpn.displayName || Cpn.name || 'Component'})`;
  return NewCpn

  // 定义函数组件
  // function NewCpn2(props) {

  // }
  // return NewCpn2
}

class HelloWorld extends PureComponent {
  render() {
    return <h1>Hello World</h1>
  }
}

//直接命名
HelloWorld.displayName = 'HelloWorldComponent';

const HelloWorldHOC = hoc(HelloWorld)

export class App extends PureComponent {
  render() {
    return (
      <div>
        <HelloWorldHOC/>
      </div>
    )
  }
}

export default App

props增强

js 复制代码
import { PureComponent } from 'react'

// 定义组件: 给一些需要特殊数据的组件, 注入props
function enhancedUserInfo(OriginComponent) {
  class NewComponent extends PureComponent {
    constructor(props) {
      super(props)

      this.state = {
        userInfo: {
          name: "clare",
          level: 1
        }
      }
    }

    render() {
      return <OriginComponent {...this.props} {...this.state.userInfo}/>
    }
  }

  return NewComponent
}

export default enhancedUserInfo
jsx 复制代码
import React, { PureComponent } from 'react'
import enhancedUserInfo from './hoc/enhanced_props'
import About from './pages/About'


const Home = enhancedUserInfo(function(props) {
  return <h1>Home: {props.name}-{props.level}-{props.banners}</h1>
})


export class App extends PureComponent {
  render() {
    return (
      <div>
        <Home banners={["轮播1", "轮播2"]}/>   
      </div>
    )
  }
}

export default App

Context共享

js 复制代码
import { createContext } from "react"

const ThemeContext = createContext()

export default ThemeContext
js 复制代码
import ThemeContext from "../context/theme_context"

function withTheme(OriginComponment) {
  return (props) => {
    return (
      <ThemeContext.Consumer>
        {
          value => {
            return <OriginComponment {...value} {...props}/>
          }
        }
      </ThemeContext.Consumer>
    )
  }
}

export default withTheme
jsx 复制代码
import React, { PureComponent } from 'react'
import withTheme from '../hoc/with_theme'
import ThemeContext from '../context/theme_context'



// export class Product extends PureComponent {
//   render() {
//     return (
//       <div>
//         Product:
//         <ThemeContext.Consumer>
//           {
//             value => {
//               return <h2>theme:{value.color}-{value.size}</h2>
//             }
//           }
//         </ThemeContext.Consumer>
//       </div>
//     )
//   }
// }

// export default Product

export class Product extends PureComponent {
  render() {
    const { color, size } = this.props

    return (
      <div>
        <h2>Product: {color}-{size}</h2>
      </div>
    )
  }
}

export default withTheme(Product)
jsx 复制代码
import React, { PureComponent } from 'react'
import ThemeContext from './context/theme_context'
import Product from './pages/Product'

export class App extends PureComponent {
  render() {
    return (
      <div>
        <ThemeContext.Provider value={{color: "red", size: 30}}>
          <Product/>
        </ThemeContext.Provider>
      </div>
    )
  }
}

export default App

登录鉴权

js 复制代码
function loginAuth(OriginComponent) {
  return props => {
    // 从localStorage中获取token
    const token = localStorage.getItem("token")

    if (token) {
      return <OriginComponent {...props}/>
    } else {
      return <h2>请先登录, 再进行跳转到对应的页面中</h2>
    }
  }
}

export default loginAuth
jsx 复制代码
import React, { PureComponent } from 'react'
import loginAuth from '../hoc/login_auth'

export class Cart extends PureComponent {
  render() {
    return (
      <h2>Cart Page</h2>
    )
  }
}

export default loginAuth(Cart)
jsx 复制代码
import React, { PureComponent } from 'react'
import Cart from './pages/Cart'

export class App extends PureComponent {
  constructor() {
    super()

    // this.state = {
    //   isLogin: false
    // }
  }

  loginClick() {
    localStorage.setItem("token", "hhh")

     this.setState({ isLogin: true })
    //this.forceUpdate()  //强制刷新用的较少
  }

  render() {
    return (
      <div>
        App
        <button onClick={e => this.loginClick()}>登录</button>
        <Cart/>
      </div>
    )
  }
}

export default App

二. 缺陷

  • 嵌套问题:需要包裹原组件,大量使用会产生深层嵌套
  • 调试困难:多层嵌套让props来源难以追踪
  • props劫持:可能意外覆盖传入的props(如name属性被覆盖)
  • 适用场景:类组件中仍常见,函数组件推荐使用Hooks

三. 其余高阶组件函数

memo组件作用

当父组件重新渲染时,React 默认会递归渲染所有子组件。memo 可以阻止子组件在 props 没有变化 时的重新渲染。

diff 复制代码
-   功能:类似PureComponent,对props进行浅比较(shallow compare),
-   原理:比较前后props差异决定是否重新渲染
-   本质:就是一个高阶组件,接收组件返回增强后的组件
jsx 复制代码
import { useState, memo } from 'react';

// 使用 memo 包装子组件
const ChildComponent = memo(function ChildComponent({ name }) {
  console.log('ChildComponent 渲染了'); // 只有 name 变化时才会打印
  return <div>Hello, {name}!</div>;
});

function ParentComponent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('Alice');

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        计数: {count}
      </button>
      <button onClick={() => setName('Bob')}>
        改名
      </button>
      <ChildComponent name={name} />
    </div>
  );
}

注意事项

jsx 复制代码
// 注意:如果传递对象、数组或函数,memo 可能失效
const ChildComponent = memo(function ChildComponent({ user, onClick }) {
  console.log('ChildComponent 渲染了');
  return <div onClick={onClick}>Hello, {user.name}!</div>;
});

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  // 每次都会创建新的对象和函数,导致 memo 失效
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>计数</button>
      <ChildComponent 
        user={{ name: 'Alice' }}  // 每次都创建新对象
        onClick={() => {}}         // 每次都创建新函数
      />
    </div>
  );
}

解决 :使用 useMemouseCallback 来保持引用稳定。

useMemo vs useCallback 对比

特性 useMemo useCallback
缓存函数 useMemo(() => fn, deps) useCallback(fn, deps)
缓存对象 useMemo(() => obj, deps) 不适用
返回值 缓存函数返回的值 直接缓存函数本身
等价关系 useCallback(fn, deps) = useMemo(() => fn, deps)

forwardRef作用

diff 复制代码
-   问题背景:函数组件无实例,无法直接绑定ref
-   解决方案:使用forwardRef将ref作为第二个参数传递
-   限制:仅适用于函数组件,类组件使用会报错

其余用法可查看: juejin.cn/post/718658...

jsx 复制代码
import { useRef, forwardRef } from 'react';

// 简单的按钮组件
const FancyButton = forwardRef((props, ref) => {
return (
  <button ref={ref} style={{ padding: '10px 20px' }}>
    {props.children}
  </button>
);
});

function App() {
const buttonRef = useRef(null);

const focusButton = () => {
  buttonRef.current.focus(); // 聚焦按钮
};

return (
  <div>
    <FancyButton ref={buttonRef}>点击我</FancyButton>
    <button onClick={focusButton}>让上面按钮获得焦点</button>
  </div>
);
}
相关推荐
TechFrank3 小时前
Shadcn/ui 重磅更新:7 个实用新组件深度解析与实战指南
前端
快乐是一切3 小时前
PDF中的图像与外部对象
前端
前端开发呀3 小时前
无所不能的uniapp拦截器【三】uni-app 拦截器核心流程解析
前端·javascript·微信小程序
今天周二3 小时前
vite 将react老项目中的没有兼容处理的写法转成兼容性写法
react.js
云枫晖3 小时前
破壁前行:深度解析前端跨域的本质与实战
前端·浏览器
文心快码BaiduComate3 小时前
代码·创想·未来——百度文心快码创意探索Meetup来啦
前端·后端·程序员
小白64023 小时前
前端梳理体系从常问问题去完善-框架篇(Vue2&Vue3)
前端
云和数据.ChenGuang3 小时前
vue中构建脚手架
前端·javascript·vue.js
千与千寻酱3 小时前
排列与组合在编程中的实现:从数学概念到代码实践
前端·python