1、认识高阶组件

高阶组件的本质是一个函数,它接收一个组件作为参数,返回一个新的组件,这就是高阶组件。 简单的理解:就是将一个组件进行相对于复杂的处理、增强之后,返回一个新的组件,我们在项目中就可以使用这个新的组件。

高阶组件的简单使用:

  1. import React, { PureComponent } from 'react'
  2. // 定义一个组件
  3. class App extends PureComponent {
  4. render() {
  5. console.log(this.props);
  6. return (
  7. <div>
  8. <h2>App组件--{ this.props.name }</h2>
  9. </div>
  10. )
  11. }
  12. }
  13. // 给组件取一个别名 好玩
  14. App.displayName = "weiweihu"
  15. // 定义一个函数 实际上就是我们说的高阶组件 接收一个组件作为参数 返回一个新的组件
  16. function enhanceComponent(WrappedComponent) {
  17. // 定义一个组件this.props
  18. class NewComponent extends PureComponent {
  19. render() {
  20. console.log('组件传的值', this.props);
  21. // 将组件传递的值 向下进行传递
  22. return <WrappedComponent { ...this.props }/>
  23. }
  24. }
  25. // 给返回的组件一个别名
  26. NewComponent.displayName = "coder"
  27. // 将组件返回
  28. return NewComponent
  29. }
  30. const EnhanceComponent = enhanceComponent(App)
  31. // 将新组件进行导出
  32. export default EnhanceComponent
  33. // 使用导出的组件 并对组件进行传值
  34. import EnhanceComponent from './EnhanceComponent'
  35. // 使用组件
  36. ReactDOM.render(<EnhanceComponent name="coderweiwei"/>, document.getElementById('root'))

小知识: 我们声明或者定义的组件可以重新起一个展示名(别名)。语法: App.displayName = “NewApp” 不管是类组件还是函数式组件 都是可以起一个展示的名字的

类组件进行定义的时候,写成类的表达式的时候,可以省略类名

  1. class App extends Component {}
  2. // 等价于下面的语句 是同样的效果
  3. const App = class extends Component {}
  4. // 和函数的定义是一样的
  5. function sum() {}
  6. => 等价于
  7. const sum = function() {}
  8. => 等价于
  9. const sum = () => {}

2、高阶组件的使用-props的增强

在使用组件的时候,如果我们需要给组件统一增加一个属性props,那么我们可以使用高阶组件来实现这一效果,就是高阶组件拿到我们传入的组件,然后在组件返回的时候,向组件中插入新的属性,那么我们新的组件就拥有新的props了,在组件中就可以直接进行使用了。

  1. import React, { PureComponent } from 'react'
  2. class Home extends PureComponent {
  3. render() {
  4. return (
  5. <div style={{ backgroundColor: 'pink' }}>
  6. <h2>home组件</h2>
  7. <h2>姓名:{ this.props.name }</h2>
  8. <h2>年龄:{ this.props.age }</h2>
  9. <h2>国籍:{ this.props.region }</h2>
  10. </div>
  11. )
  12. }
  13. }
  14. class About extends PureComponent {
  15. render() {
  16. return (
  17. <div style={{ backgroundColor: 'orange' }}>
  18. <h2>about组件</h2>
  19. <h2>姓名:{ this.props.name }</h2>
  20. <h2>年龄:{ this.props.age }</h2>
  21. <h2>国籍:{ this.props.region }</h2>
  22. </div>
  23. )
  24. }
  25. }
  26. // 高阶函数 将所有的组件新增加一个gender属性
  27. // 方法1 类组件的写法
  28. // function enhanceComponentProps(WrappedComponent) {
  29. // class NewComponent extends PureComponent {
  30. // render() {
  31. // // 向传入的组件新增加一个gender属性
  32. // return <WrappedComponent {...this.props} region="中国"/>
  33. // }
  34. // }
  35. // // 将组件进行返回
  36. // return NewComponent
  37. // }
  38. // // 将我们需要增强的组件 仅从统一的处理
  39. // const EnhanceHome = enhanceComponentProps(Home)
  40. // const EnhanceAbout = enhanceComponentProps(About)
  41. // 方法2 函数式组件的写法
  42. // function enhanceComponentProps2(WrappedComponent) {
  43. // function NewComponent(props) {
  44. // return <WrappedComponent {...props} region="中国"/>
  45. // }
  46. // return NewComponent
  47. // }
  48. // 上述组件的简写形式
  49. function enhanceComponentProps2(WrappedComponent) {
  50. // 统一给传入的组件新添加一个region属性
  51. return props => <WrappedComponent {...props} region="中国"/>
  52. }
  53. // 统一对组件进行处理
  54. const EnhanceHome = enhanceComponentProps2(Home)
  55. const EnhanceAbout = enhanceComponentProps2(About)
  56. export default class App extends PureComponent {
  57. render() {
  58. return (
  59. <div>
  60. <EnhanceHome name="coderweiwei" age={18}/>
  61. <EnhanceAbout name="kobe" age={40}/>
  62. </div>
  63. )
  64. }
  65. }

个人理解:高阶组件就是一个普通的函数,当传入一个组件的时候,我们需要将这个传入的组件进行相应的处理,增加属性、修改数据、修改页面结构,当修改完毕后,再将此组件进行返回。那么我们得到的就是一个经高阶组件处理的新的组件。新的组件拥有新的状态、数据。

3、Context多种使用方式的复习

  1. import React, { PureComponent } from 'react'
  2. // 定义context
  3. const UserInfoContext = React.createContext({
  4. name: 'coderweiwei',
  5. age: 12,
  6. region: '中国'
  7. })
  8. class Home extends PureComponent {
  9. render() {
  10. return (
  11. <div style={{ backgroundColor: 'pink' }}>
  12. <h2>home组件</h2>
  13. <h2>姓名:{ this.context.name }</h2>
  14. <h2>年龄:{ this.context.age }</h2>
  15. <h2>国籍:{ this.context.region }</h2>
  16. </div>
  17. )
  18. }
  19. }
  20. // 方法1 订阅context 订阅之后 可以在组件中直接使用this.context进行访问
  21. Home.contextType = UserInfoContext
  22. // 方法2 在组件内部进行context内容的分发
  23. class About extends PureComponent {
  24. render() {
  25. return (
  26. <UserInfoContext.Consumer>
  27. {
  28. userInfo => {
  29. return (
  30. <div style={{ backgroundColor: 'orange' }}>
  31. <h2>about组件</h2>
  32. <h2>姓名:{ userInfo.name }</h2>
  33. <h2>年龄:{ userInfo.age }</h2>
  34. <h2>国籍:{ userInfo.region }</h2>
  35. </div>
  36. )
  37. }
  38. }
  39. </UserInfoContext.Consumer>
  40. )
  41. }
  42. }
  43. export default class App extends PureComponent {
  44. render() {
  45. return (
  46. <div>
  47. <UserInfoContext.Provider value={{ name: 'weiwei', age: 123, region: '中国' }}>
  48. <Home/>
  49. <About/>
  50. </UserInfoContext.Provider>
  51. </div>
  52. )
  53. }
  54. }

4、高阶组件与Context搭配使用

  1. import React, { PureComponent } from 'react'
  2. // 在高阶组件中使用context
  3. const UserInfoContext = React.createContext({
  4. name: 'coderweiwei',
  5. age: 12,
  6. region: '中国'
  7. })
  8. class Home extends PureComponent {
  9. render() {
  10. return (
  11. <div style={{ backgroundColor: 'pink' }}>
  12. <h2>home组件</h2>
  13. <h2>姓名:{ this.props.name }</h2>
  14. <h2>年龄:{ this.props.age }</h2>
  15. <h2>国籍:{ this.props.region }</h2>
  16. </div>
  17. )
  18. }
  19. }
  20. class About extends PureComponent {
  21. render() {
  22. return (
  23. <div style={{ backgroundColor: 'orange' }}>
  24. <h2>about组件</h2>
  25. <h2>姓名:{ this.props.name }</h2>
  26. <h2>年龄:{ this.props.age }</h2>
  27. <h2>国籍:{ this.props.region }</h2>
  28. </div>
  29. )
  30. }
  31. }
  32. // 高阶组件 组件都是消费者 可将context里面的数据 传入到新的组件 新的组件处理数据后 并将新的组件返回
  33. function enhanceContentToComponent(WrappedComponent) {
  34. return props => {
  35. return (
  36. <UserInfoContext.Consumer>
  37. {
  38. userInfo => <WrappedComponent {...props} {...userInfo}/>
  39. }
  40. </UserInfoContext.Consumer>
  41. )
  42. }
  43. }
  44. // 将Home组件和About组件进行统一处理
  45. const EnhanceHome = enhanceContentToComponent(Home)
  46. const EnhanceAbout = enhanceContentToComponent(About)
  47. export default class App extends PureComponent {
  48. render() {
  49. return (
  50. <div>
  51. <UserInfoContext.Provider value={{ name: '伟伟', age: 123456, region: '中国' }}>
  52. <EnhanceHome/>
  53. <EnhanceAbout/>
  54. </UserInfoContext.Provider>
  55. </div>
  56. )
  57. }
  58. }

5、高阶组件的使用-鉴权操作

鉴权:我们在进入某个页面的之前会对访问者的权限进行控制,比如说没有登录之前是不能访问个人中心页面和购物车页面的,只有当我们登录了,才有访问某些页面的权限。 原理:利用高阶组件对需要鉴权的页面进行统一处理,判断当前页面是否有权限,利用高阶组件返回不同的新组件,如没有登录的话,就返回需要进行登录的界面。如果已经登录的话,就可以返回原始的界面。

  1. import React, { PureComponent } from 'react'
  2. // 在进入某些页面的时候 需要对当前的信息 进行鉴定 有没有登录
  3. // 登录了才会展示例如个人信息页面 没有登录的话 就会跳转到需要登录的页面
  4. class Login extends PureComponent {
  5. render() {
  6. return (
  7. <div>
  8. <h2>欢迎来到登录界面</h2>
  9. </div>
  10. )
  11. }
  12. }
  13. // 需要鉴权的组件
  14. class Cart extends PureComponent {
  15. render() {
  16. return (
  17. <div>
  18. <h2>购物车中心,这里是你的天堂</h2>
  19. </div>
  20. )
  21. }
  22. }
  23. // 高阶组件
  24. function withAuth(WrappedComponent) {
  25. return props => {
  26. // 需要对传递的数据 做相应的判断
  27. const isLogin = props.isLogin
  28. if (isLogin) {
  29. return <WrappedComponent {...props}/>
  30. } else {
  31. return <Login/>
  32. }
  33. }
  34. }
  35. // 增强后的组件
  36. const EnhanceCart = withAuth(Cart)
  37. export default class App extends PureComponent {
  38. render() {
  39. return (
  40. <div>
  41. <p>欢饮回到主页</p>
  42. <EnhanceCart isLogin={ true }/>
  43. </div>
  44. )
  45. }
  46. }

6、高阶组件的使用-劫持生命周期

在高阶函数中,我们定义类组件的时候,类组件具有相应的生命周期,我们可以通过组件的生命周期做一些相应的计算操作,可以在生命周期中函数中执行相应的操作。最后,返回新的组件。

  1. import React, { PureComponent } from 'react'
  2. // 需求: 获取组件渲染的事件
  3. class Home extends PureComponent {
  4. render() {
  5. return (
  6. <div>
  7. <p>home组件</p>
  8. </div>
  9. )
  10. }
  11. }
  12. class About extends PureComponent {
  13. render() {
  14. return <h2>我是about组件</h2>
  15. }
  16. }
  17. // 高阶组件
  18. function withRenderTime(WrappedComponent) {
  19. class HeigerOrderCpn extends PureComponent {
  20. // 开始渲染的时间
  21. UNSAFE_componentWillMount() {
  22. this.startTime = Date.now()
  23. }
  24. // 渲染结束的时间
  25. componentDidMount() {
  26. this.endTime = Date.now()
  27. const time = this.endTime - this.startTime
  28. // 每个组件都有name属性
  29. console.log(`${WrappedComponent.name}组件渲染时间`, time);
  30. }
  31. // 渲染函数
  32. render() {
  33. return <WrappedComponent/>
  34. }
  35. }
  36. // 给高阶组件一个展示名
  37. HeigerOrderCpn.displayName = "Cpn"
  38. // 将组件返回
  39. return HeigerOrderCpn
  40. }
  41. // 将组件高阶化
  42. const EnhanceHome = withRenderTime(Home)
  43. const EnhanceAbout = withRenderTime(About)
  44. export default class App extends PureComponent {
  45. render() {
  46. return (
  47. <div>
  48. app
  49. <EnhanceHome />
  50. <EnhanceAbout />
  51. </div>
  52. )
  53. }
  54. }

上面的案例: 展示的是在组件渲染的时候,我们来计算每个组件的渲染时间,那么单独的在每个组件的生命周期函数中写,代码会显得非常得冗余。如果在高阶组件中,我们得高阶组件可以劫持我们组件的生命周期函数,在组件进行渲染的时候,都会调用组件的生命周期,这样简化的代码,达到了我们想要的效果。其实就是统一的对所有组件的处理。类似于vue中的mixin(混入)。

7、在函数式组件中使用ref

原理:在函数式组件中,因为函数式组件没有生命周期,没有组件的实例对象。所以我们无法像类组件一样获取直接通过ref来获取组件的dom结构。我们使用react内部封装的高阶组件forwardRef函数类对ref绑定dom结构进行转发,转发后,ref可绑定任意组件的dom结构。

  1. import React, { PureComponent, createRef, forwardRef } from 'react'
  2. // 使用ref来访问类组件的实例
  3. class Home extends PureComponent {
  4. render() {
  5. return (
  6. <div>
  7. <p>Home组件</p>
  8. </div>
  9. )
  10. }
  11. }
  12. // 使用ref来访问函数式组件 因为函数式组件没有生命周期函数 没有this实例对象
  13. // 需要需要使用react内部封装的 高阶组件 forwardRef()
  14. const About = forwardRef((props, ref) => {
  15. // props代表的式传递的参数 ref绑定的是相应的dom结构
  16. // console.log(props, ref);
  17. return (
  18. <div ref={ ref }>
  19. <h2>测试数据</h2>
  20. </div>
  21. )
  22. })
  23. export default class App extends PureComponent {
  24. constructor() {
  25. super()
  26. this.homeRef = createRef()
  27. this.aboutRef = createRef()
  28. }
  29. render() {
  30. return (
  31. <div>
  32. <p>app组件</p>
  33. <button onClick={ () => this.btnClick() }>测试按钮</button>
  34. <Home ref={ this.homeRef }/>
  35. <hr/>
  36. <p>可以像类组件一样 给这个组件绑定ref ref经过高阶函数的处理
  37. 可以将ref进行转发 转发到函数组件的dom元素上
  38. </p>
  39. <About name='123456' ref={ this.aboutRef }/>
  40. </div>
  41. )
  42. }
  43. btnClick() {
  44. // 可以直接访问 类组件的实例
  45. console.log(this.homeRef.current);
  46. console.log(this.aboutRef.current);
  47. }
  48. }

8、portals的使用

简单的理解就是:控制组件或者元素渲染的具体位置。

  1. import React, { PureComponent } from 'react'
  2. import ReactDOM from 'react-dom'
  3. // 普通的组件
  4. class Home extends PureComponent {
  5. render() {
  6. return (
  7. <div>
  8. <p>Home组件</p>
  9. </div>
  10. )
  11. }
  12. }
  13. // 弹框组件 封装的简单的弹框组件
  14. class Modal extends PureComponent {
  15. // 这里的渲染方法是不一样的 需要使用reactDOM函数库提供的方法
  16. // ReactDOM.createPortal方法的第一个参数是需要渲染的dom结构,第二个参数是需要挂载的dom节点
  17. render() {
  18. return ReactDOM.createPortal(
  19. this.props.children,
  20. document.getElementById('modal')
  21. )
  22. }
  23. }
  24. export default class App extends PureComponent {
  25. constructor() {
  26. super()
  27. this.state = {
  28. show: true
  29. }
  30. }
  31. render() {
  32. return (
  33. <div>
  34. app组件
  35. <button onClick={ () => this.btnClick() }>显示与隐藏</button>
  36. <Home />
  37. { this.state.show ? <Modal><h2>标题</h2><p>内容1</p><p>内容2</p></Modal> : null }
  38. </div>
  39. )
  40. }
  41. btnClick() {
  42. console.log(this);
  43. this.setState({
  44. show: !this.state.show
  45. })
  46. }
  47. }
  48. # 需要在index.html中创建dom挂载的节点 以及为该节点书写css样式
  49. <body>
  50. <noscript>You need to enable JavaScript to run this app.</noscript>
  51. <div id="root"></div>
  52. <!-- 弹框组件挂载的位置 我们自己新创建的 -->
  53. <div id="modal"></div>
  54. </body>
  55. // css样式 是组件水平垂直居中 使用定位进行处理
  56. #modal {
  57. position: fixed;
  58. left: 50%;
  59. top: 50%;
  60. transform: translate(-50%, -50%);
  61. }