React hooks能替代HOC和render props么?

最近开始学习React,记录一下心得。

React hooks是16.8.0推出的,其目的是为了替换class,HOC,render props。那么本文将讨论啥是hooks,HOC,render props,以及hooks究竟能不能替换掉HOC,render props。

首先是hooks(这里指custom hooks,下同),HOC,render props到底是做什么的?

对于新手,并不是很容易理解它们是做什么的,至少我没有很容易理解。

我现在理解为它们是为了共享一些维护state的逻辑。

什么意思呢?首先它共享的是逻辑,但是共享逻辑你随便封装一个函数不就可以了么?问题就出在它是共享维护state的逻辑。普通的逻辑的和维护state的逻辑的区别就是,维护一个state的逻辑,当state发生变化时,用到这个state的组件是要重新渲染的,显然用普通的函数做不了这个事情。

那么有哪些维护state的逻辑呢?我大体上把它们分为两类:

第一类,一个状态组件维护state的逻辑,第二类,一个非组件维护state的逻辑。

其实一个状态组件本身就是在维护一个state,当用户操作它所渲染的UI的时候,state发生响应的变化。组件的逻辑负责维护state的变化。

那么非组件指什么呢?

比如说我维护一个user的登录状态,这个登录状态需要发送请求给后台去拿。那么这个登录状态就会有3个值{fetching,online,offline},而且可能会从fetching到online,或者从fetching到offline,就是会变化的。

比如说我需要记录用户鼠标的位置(x,y坐标),这个是随用户移动鼠标而变化的。

比如说我做一个定时器,每隔一段时间切换一下我维护的一个state。

等等,我相信还有其他很多。

之所以分为这两类,是因为我们不能用hooks来共享状态组件。这是因为我们用hooks的时候是需要用它的返回值的,这个返回值通常是这个hooks所维护的state或者state的计算值,那么这个hooks就不可能返回一个dom元素了,也就是hooks不能包含组件。

然后我们建立一个简单的模型分析下三者的结构。

我们假设有三个组件A,B,C需要共享一个维护state的逻辑S。那么:

hooks是什么呢?hooks就是把S封装成useS,也就是一个custom hooks,然后在A,B,或者C里面使用useS

const useS = () => {
    const [state, setState] = useState('someValue')
    useEffect(() => {
        ...some code...
        setState('anotherValue')
    })
    return state;
}
const A = () => {
    const s = useS();
    return (
        <div>
            <div>{s}</div>
            <div>A</div>
        </div>
    )
}

Render props是什么呢?它是把S封装成一个component,同时这个component留了一个props用来接收需要渲染的组件。我认为下面这两种写法都是render props,因为他们的结构基本一样,结果完全一样。

第一种,就是官方文档的写法:

const S = ({render}) => {
    const [state, setState] = useState('someValue')
    useEffect(() => {
        ...some code...
        setState('anotherValue')
    })
    return render(state)
}

const A = ({s}) => {
    return (
        <div>
            <div>{s}</div>
            <div>A</div>
        </div>
    )
}

const App = () => {
    return <S render = {(s) => <A s={s} />} />
}

第二种,也是可用的写法。

const S = ({render}) => {
    const [state, setState] = useState('someValue')
    useEffect(() => {
        ...some code...
        setState('anotherValue')
    })
    const Render = render;
    return <Render s={state} />
}

const A = ({s}) => {
    return (
        <div>
            <div>{s}</div>
            <div>A</div>
        </div>
    )
}

const App = () => {
    return <S render = {A} />
}

可以看到render props相对于hooks有明显的区别,它在被调用的时候需要知道两个component。Render props的两种写法的区别就是第一种是调用的时候render设置为一个函数,返回值为component实例。第二种是render设置为component本身。

HOC是什么呢?HOC就是把S封装成一个函数withS,这个函数返回一个新的component。

const withS =(WrappedComponent) => {
    return () => {
        const [state, setState] = useState('someValue')
        useEffect(() => {
            ...some code...
            setState('anotherValue')
        })
        return <WrappedComponent s={state} />
    }
}
const A = ({s}) => {
    return (
        <div>
            <div>{s}</div>
            <div>A</div>
        </div>
    )
}
const AWithS = withS(A);
const App = () => { return <AWithS />; }

对比一下HOC和render props,你会发现其实HOC返回的component使用的技术和render props很相似哦!只不过render props的例子中需要render的component来自props,而HOC中需要render的component来自HOC的参数。

当然在react的官方文档中你会发现另一种写法,HOC的返回值调用了用render props技术实现的S。这种写法往往是为了提供API给其他开发者,让喜欢用HOC的去使用HOC,喜欢用render props的去使用render props。

const S = ({render}) => {
    const [state, setState] = useState('someValue')
    useEffect(() => {
        ...some code...
        setState('anotherValue')
    })
    const Render = render;
    return <Render s={state} />
}

const withS = (WrappedComponent) => {
    return () => { return <S render = {(s) => <WrappedComponent s={s} />} /> }
}

const A = ({s}) => {
    return (
        <div>
            <div>{s}</div>
            <div>A</div>
        </div>
    )
}
const AWithS = withS(A);
const App = () => {
    return <AWithS />;
}
const AnotherApp = () => {
    return <S render = {(s) => <A s={s} />} />
}

HOC和hooks一样,在调用的时候只需要知道一个component,上面例子中的AwithS,实际情况是我们命名AwithS为A,而真正的A不被暴露(export)出去,这是很好的行为。

HOC不光是有共享维护state的逻辑的能力,还可以做一切封装能做的事情,比如说偷偷改下上层component传过来的props的值。

Hooks能替代HOC,render props么?

hooks是无法实现共享状态组件逻辑的,当然就无法在这方面代替HOC和render props。而共享非组件逻辑的功能,基本都可以用hooks来替代,而且最好用hooks来实现,因为我们看到了,hooks的逻辑最直接,最容易理解。当然第一原则仍然是根据业务实事求是,就是概念上该是什么就是什么,如果概念上就是HOC,那么就用HOC。实事求是是为了代码的更好维护。