关于React的高阶组件

什么是高阶组件?

高阶组件是什么?乍一听,感受是个很高级的概念,可是不要被这个名词吓到,说简单点 就是给已有的一个组件外面“包一层”。 咱们知道 “高阶函数” 是传入函数做为参数, 高阶组件 其实就是传入 组件做为参数,并返回一个新组件。html

高阶组件的做用

高阶组件的做用是什么?项目开发当中,一般咱们会把一些公用的逻辑抽离出来, 而且应用到不少组件上,给组件赋予一个新的能力,这时候就须要用到它。前端

常见的应用场景

简而言之,若是你须要给不少组件都写相同的判断逻辑,那么能够考虑提取出一个高阶组件vue

实际场景: 路由权限控制

记得之前开发vue 项目的时候也遇到过相似的问题,那时用的vue-router,实现方式是在全局的router.beforeEach方法中获取用户信息,根据用户的角色和权限跳转到不一样的页面。参考官方文档react

一个实现路由权限控制的高阶组件:git

const LodingUserTip = () => {
  return (
    <p>
      <Icon type="loading-3-quarters" />
      正在获取用户信息,稍后...
    </p>
  )
}
const UnAuthoriedTip = () => {
  return (
    <p>
      <Icon type="lock" />
      <h5 style={{color: 'red'}}>抱歉,您没有访问该页面的权限...</h5>
    </p>
  )
}

const authDecorator = WrappedComponent => {
// auth 组件是最终返回的高阶组件
  const Auth = props => {
    const {
      match: {path, params},
      currentUser: {role_type: roleType},
      userLoading,
      history,
      location,
    } = props
    const {match} = props
    // 用户信息正在加载
    if (userLoading) {
      return <LodingUserTip />
    }

    // 当前登陆用户没有访问权限
    if (!roles.includes(roleType)) {
      return <UnAuthoriedTip />
    }

    // 若是没有找到对应页面,则跳转至该权限对应的默认页面
    const {allowedUrl, defaultUrl} = AUTH_MAP[roleType]
    if (!allowedUrl.includes(path)) {
      history.push(defaultUrl)
      return null
    }
    
    // 还能够把额外的 props传递给你使用的组件
    return (
      <WrappedComponent
        params={params}
        match={match}
        location={location}
        history={history}
        roleType={roleType}
        {...props}
      />
    )
  }

  const mapStateToProps = ({
    users,
    loading: {models: {users: userLoading}},
  }) => {
    return {
      currentUser: users.currentUser,
      userLoading,
    }
  }

  return withRouter(connect(mapStateToProps)(Auth))
}

export default authDecorator
复制代码

上面这段代码即是一个高阶组件, 一般咱们会请求后端接口返回给咱们一个当前用户的roleType, 咱们在前端会存一个map,根据不一样的roleType映射到的不一样的路由, map结构以下:github

export const AUTH_MAP = {
  admin: {
    allowedUrl: [
      '/a/b',
    ],
    defaultUrl: '/f',
  },
  superAdmin: {
    allowedUrl: [
      '/a/b',
      '/c/d',
    ],
    defaultUrl: '/e',
    }
}
复制代码

使用的时候能够配合装饰器,更简单方便:vue-router

@authDecorator
class PageOneComponent extends React.Component {
...
复制代码

高阶组件中会判断 当前的用户角色与当前的路由是否匹配,若匹配不成功则显示默认信息,或者跳转到默认url(或者登录页)redux

遇到的问题

在使用antd的Form.create(onFieldsChange:(props, fields) => {}) 我很疑惑这里 onFieldsChange 为何能获取到组件的props, 最后明白: 其实Form.create()返回了一个高阶组件,会接收到全部传给组件的props, 当fields变化的时候,antd执行onFieldsChange并把props传进去即可以。segmentfault

但有一个要注意,若是一个组件有多个装饰器(须要被多个高阶组件包裹),须要注意顺序,好比下面这个图片,我但愿在onFieldsChange 中获取 从 redux 拿到的 dictionary信息,这样是获取不到的,由于 最早执行的connect, 而后才执行的 Form.create()方法,Form.create()返回的高阶组件只能获取到以后传递给组件的props. 后端

如图,debugger打断点 看到 props中 没有 dictionary

要想拿到全部的props,请按照下图这样作:(最早执行的是Form.create()返回的函数,因此后面执行的函数以及传递的props都会被onFieldsChange接收到):

须要注意的点

  • ref 没法传递的问题: 因为你使用的组件其实被包裹了一层, 因此上层组件获取的ref, 其实是获取的高阶组件。 具体详情请看个人另一篇博客关于React的ref
  • 被包裹的组件自身的静态方法默认是不会出如今高阶组件中的,要想解决这个问题,咱们能够 MyHOC.staticMethod = WrappedComponent.staticMethod 这样明确的将静态方法传递给高阶组件,可是这样太不严谨了,很容易漏掉。官方推荐的最佳实践是使用 hoist-non-react-statics 第三方库 自动copy 静态方法
  • 高阶组件(HOC)应该是无反作用的纯函数,且不该该修改原组件
  • 给高阶组件函数传参除了传入 一个组件 还想传入 其余参数怎么办? 我以为能够写成 相似 connect的柯里化的形式比较好。 本文中使用到的高阶组件基本都是单参数,若是想传递多个参数也是没问题的,能够看官网的例子, 例子中selectData 即是第二个参数,做为获取数据的回调,获取数据的逻辑即可以从高阶组件中解耦。

参考连接

hcysun.me/2018/01/05/… segmentfault.com/a/119000000… segmentfault.com/a/119000000… www.zhihu.com/question/58… hacpai.com/article/151…

相关文章
相关标签/搜索