林大夏 2019-12-09
最近前几个月开始,新项目都开始完全使用typescript+hooks,先不说typescript吧,hooks是真的香??
现在前端项目的组件化,一般都是基于最基础的UI组件库(里面也有组件的功能逻辑),加上业务逻辑,封装一个个component,container。
组件是 UI + 逻辑的复用,但逻辑复用能力等于 0。
而在项目中,很难做到轻松的把 UI 和逻辑分开。(这里的逻辑并不是简单的能抽离的那种工具函数)。在此之前,React有Mixin(ES6Class就已经废弃了,不谈了),HOC,render props来抽离逻辑。
HOC并不是react提供的api,而是基于react特性的一种模式,常见的例子就是大家常见的react-redux中的connect函数,antd的form.create函数,HOC是一个函数,参数接受一个组件,返回一个新组件
render props 我理解是平级组件之间单向依赖的一种模式,和HOC一样也不是什么react提供的api,也是一种模式,由React Trainning成员的一个大佬提出替代HOC的一种更好的模式。首先需要一个提供可变数据源的组件,然后给这个组件传入一个叫render函数的props(叫啥名字都行,提出者只是想强调这是一个拥有类似render功能的props..),特点就是这个props属性不是对象不是简单类型变量,就是一个函数,这个函数的参数就是希望用到可变数据源中的数据,返回结果就是一个jsx的UI结构。如果替代HOC,render的参数就可以是一个组件,react router4的withRouter即是这样实现:
import React from 'react' import PropTypes from 'prop-types' import hoistStatics from 'hoist-non-react-statics' import Route from './Route' /** * A public higher-order component to access the imperative API */ const withRouter = (Component) => { const C = (props) => { const { wrappedComponentRef, ...remainingProps } = props return ( <Route render={routeComponentProps => ( <Component {...remainingProps} {...routeComponentProps} ref={wrappedComponentRef}/> )}/> ) } C.displayName = `withRouter(${Component.displayName || Component.name})` C.WrappedComponent = Component C.propTypes = { wrappedComponentRef: PropTypes.func } return hoistStatics(C, Component) } export default withRouter
仔细体会render props的用处和例子,可参考:1 2
HOOK是真正意义上的复用逻辑的武器。render props是把嵌套的HOC改成了一种平级传render函数的props的方法,但是当状态多个复用的时候,就也会变成不断嵌套的难看的结构。这里引用一下蚂蚁的一篇文章的例子:
<WindowSize> (size)=> ( <Mouse> (position)=> <OurComponent size={size} mouse={position}/> </Mouse> ) </WindowSize>
又想监听size又想监听position,甚至还有scroll状态的时候。。。
使用react的hook模式,比如react-use库,我们只用一行代码就解决了:
const mousePosition = useMouse(ref);
且对typescript支持更好,我们可以在use函数中做类型校注。
参考:
react-use可看相关源码参考实现,秒啊
蚂蚁金服的文章
官方react自定义hook介绍
总的来说,hook目前还算完美干净的结局了逻辑重用的问题,把react组件化思想又深进了一步,且把页面与逻辑拆开也有利于代码的长期维护啦。