• React组件进阶--render-props,render props模式的使用步骤,children代替render属性,传递props


    1.render-props

    1.React组件复用概述

    1. 思考:如果两个组件中的部分功能相似或相同,该如何处理?
    2. 处理方式:复用相似的功能(联想函数封装)
    3. 复用什么?1. state 2. 操作state的方法 (组件状态逻辑 )
    4. 两种方式:1. render props模式 2. 高阶组件(HOC)
    5. 注意:这两种方式不是新的API,而是利用React自身特点的编码技巧,演化而成的固定模式(写法)

    2.render props 模式

    1. 思路:将要复用的state和操作state的方法封装到一个组件中
    2. 问题1:如何拿到该组件中复用的state?
    3. 在使用组件时,添加一个值为函数的prop,通过 函数参数 来获取(需要组件内部实现)
      <Mouse render={(mouse) => {}}/>
      
    4. 问题2:如何渲染任意的UI?
    5. 使用该函数的返回值作为要渲染的UI内容(需要组件内部实现)
      1. <Mouse render={(mouse) => (
      2. <p>鼠标当前位置 {mouse.x},{mouse.y}p>
      3. )}/>

    3.render props模式的使用步骤

    1. 创建Mouse组件,在组件中提供复用的状态逻辑代码(1. 状态 2. 操作状态的方法)

      1. class Mouse extends React.Component {
      2. // 鼠标位置state
      3. state = {
      4. x: 0,
      5. y: 0
      6. }
      7. // 鼠标移动事件的事件处理程序
      8. handleMouseMove = e => {
      9. this.setState({
      10. x: e.clientX,
      11. y: e.clientY
      12. })
      13. }
      14. // 监听鼠标移动事件
      15. componentDidMount() {
      16. window.addEventListener('mousemove', this.handleMouseMove)
      17. }
      18. render(){
      19. //这一步先写null,后面会完善
      20. return null
      21. }
      22. }
    2. 将要复用的状态作为 props.render(state) 方法的参数,暴露到组件外部

      1. render() {
      2. return this.props.render(this.state)
      3. }
      4. <Mouse render={(mouse) => {return ()}} />
    3. 使用 props.render() 的返回值作为要渲染的内容

      1. render={(mouse) => {
      2. return (
      3. <p>
      4. 鼠标位置:{mouse.x},{mouse.y}
      5. p>
      6. )

      只实现了状态结构的复用,并没有实现UI结构的复用

    4. 在鼠标上加上图片,跟随鼠标一起移动

    5. 导入图片资源

      import img from './images/star.png'
      
    6. 将图片写下以下内容

      1. {/* 图片 */}
      2. <Mouse render={mouse => {
      3. return <img src={img} alt="star" style={{
      4. position: 'absolute',
      5. //减64是因为图片128*128想要鼠标在图片中心则需要-64
      6. top: mouse.y - 64,
      7. left: mouse.x - 64
      8. }} />
      9. }}>Mouse>

    4.children代替render属性

    1. 注意:并不是该模式叫 render props 就必须使用名为render的prop,实际上可以使用任意名称的prop
    2. 把prop是一个函数并且告诉组件要渲染什么内容的技术叫做:render props模式
    3. 推荐:使用 children 代替 render 属性
    1. //组件内部
    2. render() {
    3. return this.props.children(this.state)
    4. }
    5. <Mouse>
    6. {mouse => {
    7. return (
    8. <p>
    9. 鼠标位置:{mouse.x},{mouse.y}
    10. p>
    11. )
    12. }}
    13. Mouse>
    14. <Mouse >{mouse =>
    15. (<img src={img} alt="star" style={{
    16. position: 'absolute',
    17. top: mouse.y - 64,
    18. left: mouse.x - 64
    19. }}
    20. />)
    21. }
    22. Mouse>

     Context 中的用法:

    1. <Consumer>
    2. {data => <span>data参数表示接收到的数据 -- {data}span>}
    3. Consumer>

    代码优化

    1. 推荐:给 render props 模式添加 props校验

      1. import propTypes from 'prop-types'
      2. // 添加props校验
      3. Mouse.propTypes = {
      4. children:propTypes.func.isRequired
      5. }
    2. 应该在组件卸载时解除 mousemove 事件绑定

      因为componentDidMount是我们自己手动开的,既然开了一个,卸载时,就要关这一个

      1. // 推荐:在组件卸载时移除事件绑定
      2. componentWillUnmount(){
      3. window.addEventListener('mousemove',this.handleMouseMove)
      4. }

    2.高阶组件

    1.概述

    • 目的:实现状态逻辑复用
    • 采用 包装(装饰)模式 ,比如说:手机壳
    • 手机:获取保护功能
    • 手机壳 :提供保护功能
    • 高阶组件就相当于手机壳,通过包装组件,增强组件功能

    2.思路分析

    1. 高阶组件(HOC,Higher-Order Component)是一个函数,接收要包装的组件,返回增强后的组件
    2. 高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,
      1. class Mouse extends React.Component {
      2. render() {
      3. return <WrappedComponent {...this.state} />
      4. }
      5. }
    3. 通过prop将复用的状态传递给被包装组件 WrappedComponent
      const EnhancedComponent = withHOC(WrappedComponent)
      

    3.高阶组件使用步骤

    1. 创建一个函数,名称约定以 with 开头

      function withMouse() {}
      
    2. 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)

      function withMouse(WrappedComponent) {}
      
    3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回

      1. function withMouse(WrappedComponent) {
      2. class Mouse extends React.Component {。。。}
      3. return Mouse
      4. }
    4. 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件

      UI组件由WrappedComponent提供

      1. // Mouse组件的render方法中:
      2. //Mouse组件的状态->...this.state
      3. return <WrappedComponent {...this.state} />
    5. 调用该高阶组件,传入要增强的组件,通过返回值拿到增强后的组件,并将其渲染到页面中

      1. //Positon组件作为参数传进来
      2. // 创建组件
      3. const MousePosition = withMouse(Position)
      4. // 渲染组件
      5. <MousePosition />
    6. 导入图片

      import img from './images/star.png'
      
    7. 图片的组件

      1. const Star = props =>(
      2. <img src={img} alt="" style={{
      3. position:"absolute",
      4. top:props.y -64,
      5. left:props.x -64
      6. }} />
      7. )
    8. 让图片跟随鼠标移动

      1. //调用高阶组件来增强图片的组件
      2. const MouseStar = withMouse(Star)
      3. {/* 渲染增强后的组件 */}
      4. <MouseStar>MouseStar>
    9. 完整代码

      1. // 创建高阶组件
      2. function withMouse(WrappedComponent) {
      3. // 该组件提供复用的状态逻辑
      4. class Mouse extends React.Component {
      5. // 鼠标状态
      6. state = {
      7. x: 0,
      8. y: 0
      9. }
      10. // 更新状态逻辑
      11. handleMouseMove = e => {
      12. this.setState({
      13. x: e.clientX,
      14. y: e.clientY
      15. })
      16. }
      17. // 控制鼠标状态的逻辑
      18. componentDidMount() {
      19. window.addEventListener('mousemove', this.handleMouseMove)
      20. }
      21. //在组件卸载时移除事件绑定
      22. componentWillUnmount() {
      23. window.addEventListener('mousemove', this.handleMouseMove)
      24. }
      25. render() {
      26. return <WrappedComponent {...this.state}>WrappedComponent>
      27. }
      28. }
      29. return Mouse
      30. }
      31. // 用来测试高阶组件
      32. const Position = props => (
      33. <p>
      34. 鼠标当前位置:(x:{props.x},y:{props.y})
      35. p>
      36. )
      37. // 获取增强后的组件
      38. const MousePosition = withMouse(Position)
      39. class App extends React.Component {
      40. render() {
      41. return (
      42. <div>
      43. <h1>高阶组件h1>
      44. {/* 渲染增强后的组件 */}
      45. <MousePosition>MousePosition>
      46. div>
      47. )
      48. }
      49. }

    4.设置displayName

    • 使用高阶组件存在的问题:得到的两个组件名称相同
    • 原因:默认情况下,React使用组件名称作为 displayName
    • 解决方式:为 高阶组件 设置 displayName 便于调试时区分不同的组件
    • displayName的作用:用于设置调试信息(React Developer Tools信息)
    • 设置方式:
    1. //放在创建高阶组件内部
    2. Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`
    3. 设置 getDisplayName()
    4. function getDisplayName(WrappedComponent) {
    5. return WrappedComponent.displayName || WrappedComponent.name || 'Component'
    6. }

    由上图变成下图:

    5.传递props

    • 问题:props丢失
    • 原因:高阶组件没有往下传递props
    • 解决方式:渲染 WrappedComponent 时,将 state 和 this.props 一起传递给组件
    • 传递方式:
    <WrappedComponent {...this.state} {...this.props} />
    

    总结

    1. 组件通讯是构建 React 应用必不可少的一环。
    2. props 的灵活性让组件更加强大。
    3. 状态提升是React组件的常用模式。
    4. 组件生命周期有助于理解组件的运行过程。
    5. 钩子函数让开发者可以在特定的时机执行某些功能。
    6. render props模式和高阶组件都可以实现组件状态逻辑复用。
    7. 组件极简模型: (state, props) => UI
  • 相关阅读:
    數據集成平台:datax將MySQL數據以query方式同步到hive
    我居然被TransmittableThreadLocal框架作者评论了
    Vmware下的虚拟机NAT连接后仍然木有网络
    pnpm 是凭什么对 npm 和 yarn 降维打击的
    Python如何使用PyMySQL连接数据库
    性能优化:TCP连接优化之四次挥手
    3年经验,光靠自动化测试基础,你可能连17k的测试岗都找不到,认清现实.....
    git branch -r 远程分支显示不全
    解决WPF+Avalonia在openKylin系统下默认字体问题
    OptaPlanner 发展方向与问题
  • 原文地址:https://blog.csdn.net/m0_54088431/article/details/126678502