React hooks能替代HOC和render props么?
最近开始学习React,记录一下心得。
React hooks是16.8.0推出的,其目的是为了替换class,HOC,render props。那么本文将讨论啥是hooks,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。实事求是是为了代码的更好维护。