React Hooks的丑陋一面( 二 )


使用Funclass,我们可以编写如下代码:
function Foo() {useA();useB();useC();}看起来有点干净,但是是吗?我们还需要在某个地方写3个不同的useEffect钩子,所以最后我们要写更多的代码,看看我们在这里做了什么——有了类组件,你可以一目了然地知道组件在mount上做什么 。在Funclass的例子中,你需要按照钩子并尝试搜索带有空依赖项数组的 useEffect,以了解组件在mount上做什么 。生命周期方法的声明性本质上是一件好事,我发现研究Funclasss的流程要困难得多 。我见过很多案例是Funclasses让开发者更容易写出糟糕的代码,我们后面会看到一个例子 。
但是首先,我必须承认 useEffect 有一些好处,请看以下示例:
useEffect(() => {subscribeToA();return () => {unsubscribeFromA();}; }, []);useEffect 钩子让我们将订阅和退订逻辑配对在一起 。这其实是一个非常整洁的模式,同样的,把 componentDidMount 和 componentDidUpdate 配对在一起也是如此 。以我的经验,这些情况并不常见,但它们仍然是有效的用例,在这里 useEffect 确实很有用 。问题是,为什么我们必须使用Funclass才能获得 useEffect?为什么我们的Class不能有类似的东西?答案是我们可以:
class Foo extends React.Component {someEffect = effect((value1, value2) => {subscribeToA(value1, value2);return () => {unsubscribeFromA();};})render(){this.someEffect(this.props.value1, this.state.value2);return <Text>Hello world</Text>}}effect 函数将记住给定的函数,并且仅当其参数之一已更改时才会再次调用它 。通过从我们的render函数内部触发效果,我们可以确保它在每次渲染/更新时都被调用,但只有当它的一个参数被改变时,给定的函数才会再次运行,所以我们在结合 componentDidMount 和 componentDidUpdate 方面实现了类似 useEffect 的效果,但遗憾的是,我们仍然需要在 componentWillUnmount 中手动进行最后的清理 。另外,从render内调用效果函数也有点丑 。为了得到和useEffect完全一样的效果,React需要增加对它的支持 。
最重要的是 useEffect 不应该被认为是进入funclass的有效动机,它本身就是一个有效的动机,也可以为类实现 。
动机4:性能React团队说类很难优化和最小化,funclass应该以某种方式改进,关于这件事,我只有一件事要说——给我看看数字 。
我至今找不到任何论文,也没有我可以克隆并运行以比较Funclasses VS Class的性能的基准演示应用程序 。事实上,我们没有看到这样的演示并不奇怪——Funclasses需要以某种方式实现这个功能(如果你喜欢的话,也可以用Ref),所以我很期待那些让类难以优化的问题,也会影响到Funclasses 。
不管怎么说,所有关于性能的争论,在不展示数据的情况下实在是一文不值,所以我们真的不能把它作为论据 。
动机5:Funclass不太冗长你可以找到很多通过将Class转换为Funclass来减少代码的例子,但大多数甚至所有的例子都利用了 useEffect 钩子,以便将 componentDidMount 和 componentWillUnmount 结合在一起,从而达到极大的效果 。
【React Hooks的丑陋一面】但正如我前面所说,useEffect 不应该被认为是Funclass的优势,如果忽略它所实现的代码减少,那么只会留下非常小的影响 。而且,如果你尝试使用 useMemo,useCallback 等来优化Funclass,你甚至可能得到比等效类更冗长的代码 。
当比较小而琐碎的组件时,Funclasses毫无疑问地赢了,因为类有一些固有的模板,无论你的类有多小你都需要付出 。但在比较大的组件时,你几乎看不出差别,有时正如我所说,类甚至可以更干净 。
最后,我不得不对 useContext 说几句:useContext其实比我们目前原有的类的context API有很大的改进 。但是再一次,为什么我们不能为类也有这样漂亮而简洁的API呢? 为什么我们不能做这样的事情 。
//inside "./someContext" :export const someContext = React.Context({helloText: 'bla'});//inside "Foo":import {someContext} from './someContext';class Foo extends React.component {render() {<View><Text>{someContext.helloText}</Text></View>}}当上下文中的 helloText 发生变化时,组件应该重新渲染以反映这些变化 。就是这样,不需要丑陋的高阶组件(HOC) 。
那么,为什么React团队选择只改进useContext API而不是常规content API?我不知道,但这并不意味着Funclass本质上更干净 。这意味着React应该通过为类实现相同的API改进来做得更好 。


推荐阅读