2twdtl

到现在为止,告知读者,你肯定瞥了一眼,脱脂通过,或至少书签半打的文章约16.8作出反应的最热切期待的功能:挂钩。你可能已经看到或听到有关他们是多么伟大,他们是多么可怕的,甚至如何迷惑他们。你可能会问自己“我为什么要学这个?”而你可能希望有一个更好的答案比“因为它是新的东西”。如果你竟然沿着几个钩导游跟随,你可能会发现自己在问“但为什么?我可以使用类做同样的事情!”

82BCA13D-8A45-4F10-822B-34D567B52973
信用@lizandmollie

如果这听起来很熟悉,这可能是因为这是我们通过每一个我们正在面临着不得不学习新的东西去的时间周期相同。学习新的东西可以为任何人是困难的,重新学习的东西你已经知道可能是特别令人沮丧。你的本能反应可能是在你已经知道的术语帧新的东西。当我第一次分享我对钩的学习收获到我的团队在OkCupid,我做了一个图表映射组件的生命周期方法挂钩的替代品,这让我看起188bet金宝搏官网来像这个家伙一点:

tumblr_o16n2kBlpX1ta3qyvo1_1280
作者注:道歉的OkCupid网络团队成为我的豚鼠188bet金宝搏官网

事实证明,这是学习钩子一个非常混乱的方式!很多概念没有很好地映射,或不必要地显得更为复杂的一个方法与另一个。而不是继续说说我的失败,我会得到的好东西。这并非是一个全面的指南约挂钩的一切,但我希望,一旦你读完,你会觉得有兴趣,足以想写你用钩子第一个组件。根据我的经验,这是真正的秘密:它不一定会点击,直到你开始写他们自己。事不宜迟,这是一个最好的*办法人类已知的学习挂钩**。

*我的意思是它的好
**我个人而言,已经通过发布时间发现


累了:的setState

有线:​​useState

我们从基础开始。你可能已经看到了这个钩子解释说,如果是这样,随意跳过下一节,我们开始变得更深入。

其中一个我们学会做作出反应的第一件事情就是让一个状态组件。你和我一样,学会了通过扩展来写一个组成部分React.Component(更可能是,使用React.createClass,但我们不谈论那些黑暗的日子)。你学会了使用this.setState({someKey:someValue中})修改组件的状态,请记住,键/值对你传递到的setState与你的新值覆盖旧的状态,一切以被合并了。哦,没有忘记初始化状态对象,所以我们当我们尝试不要错误的setState。当然,不要忘了.bind那将会在构造函数中被修改的状态,或记住使用箭头函数的语法有人在你的团队的每个功能安装插件通天了几年前。

tumblr_inline_p2dyybPLrV1qgoj6i_540

让我们忘掉这一切一秒钟。让我们说明一下我们需要构建,比方说,一个简单的,有状态的点击计数成分:

  1. 我们需要知道当前点击次数(我们称之为CURRENTCOUNT
  2. 我们需要一种方法来增加点击次数(我们称之为setCurrentCount

如果我们想象第二,我们有这些先决条件,我们可能会写这样的事情:

进口从“反应”反应;常量计数器=()=> {返回(
当前计数:{CURRENTCOUNT} <按钮的onClick = {()=> setCurrentCount(CURRENTCOUNT + 1)}>增量 );};

通常情况下,我们很可能达到的setState变成现实,这意味着具有重构这个微小功能部件成完全成熟的类组分此。但挂在,这就是我们的第一钩的用武之地。

进口反应,{useState}从 “反应”;常量计数器=()=> {常量[CURRENTCOUNT,setCurrentCount] = useState(0);返回(
当前计数:{CURRENTCOUNT} <按钮的onClick = {()=> setCurrentCount(CURRENTCOUNT + 1)}>增量 );};

“嗯,你到底刚刚发生?!”你可能会问自己。寒意,自我。这是一个钩子!钩子允许功能组件挂接到设有以前只提供给类组分,如状态。

useState钩是一个函数,它在一个参数:初始状态(在这种情况下,0),并返回到您为状态值的值,并且设定器中的阵列,以该顺序。当你调用的setter,阵营重新呈现组件并提供更新的状态值,就像它将如果你叫的setState

“为什么阵列解构?”你问?那么,这种方式可以命名值和setter你想要什么的挫折感。当然,你可以使用useState勾多次,你的组件中喜欢,所以,你可以保持多条状态的跟踪,如果你需要,而无需你的状态表示转换成一个对象。我们将了解更多有关这使我们在下一节中的机会。

累了:单个对象保持状态

连线:单独分开考虑状态。

一个关于巧妙的事情useState是您的组件的状态表示不是一个对象,它可以是一个数字,字符串,还是真的任何你想(包括对象)。但是,这是否意味着增加新的状态属性?比方说,以后你决定你需要跟踪之间的状态属性。当国家是一个对象,这是那么容易,因为增加的另一个关键。现在,它是那样简单添加另一个调用useState

进口反应,{useState}从 “反应”;常量计数器=()=> {常量[CURRENTCOUNT,setCurrentCount] = useState(0);常量[isClicking,setIsClicking] = useState(假);返回(
当前计数:{CURRENTCOUNT}是点击:{isClicking} <按钮的onClick = {()=> setCurrentCount(CURRENTCOUNT + 1)} onmousedown事件= {()=> setIsClicking(真)} onMouseUp {()=> setIsClicking(假)}>增量 );};

这也赋予我们让我们的代码组相关的块在一起,而不是潜在的分组无关的状态变化到一个灵活性的setState呼叫。例如,如果我想将一些事件处理程序办理纳税申报块,我可以组与像这样的最合适的代码:

进口反应,{useState}从 “反应”;常量计数器=()=> {常量[CURRENTCOUNT,setCurrentCount] = useState(0);常量incrementCounter =()=> setCurrentCount(CURRENTCOUNT + 1);常量[isClicking,setIsClicking] = useState(假);常量onmousedown事件=()=> setIsClicking(真);常量onMouseUp =()=> setIsClicking(假)返回(
当前计数:{CURRENTCOUNT}是点击:{isClicking} <按钮的onClick = {incrementCounter} onmousedown事件= {onmousedown事件} onMouseUp {onMouseUp}>增量);};

酷吧?在传统的阶级成分,这些对国家性质必须共同生活在一个单一的对象,并且状态的初始化和修改它很可能会在你的组件传播,而不是相关的逻辑组合在一起的功能。对于一个组件这个简单,收益可能较小,但对于较大的部件,它真的可以让你的组件的可读性差。

累了:生命周期方法

连线:数据发生变化时,你就需要改变

因此,我们已经学会了useState可以利用的地方(在某些方面改善时)的setState。但是,所有其他的什么强大的东西,我们可以在类组件生命周期的方法呢?这就是事情可以得到一点毛茸茸的有经验的开发人员做出反应的学习挂钩。

生命周期方法是一种抽象,让我们觉得在渲染我们正处在一个组件的哪个阶段的条款。像名字componentDidMountcomponentDidUpdatecomponentWillUnmount直观感觉对我们这些已经使用它们多年来一直和可靠的确切地知道什么时候,为什么他们会运行的东西,可以为初学者学习非常混乱。这就是说,在我的经验,我发现我们通常在几个预测的模式中使用它们。举起你的手,如果这些听起来很熟悉的:

  1. componentDidMountcomponentWillUnmount对于附接/删除事件侦听器,或设置/清除超时。(实施例:听文档滚动或按键事件而改变状态)
  2. componentDidMountcomponentDidUpdate加载基于道具/状态改变的东西。(实施例:当我们着陆页面上,并重新加载数据加载时的状态变化)
  3. componentDidMountcomponentDidUpdate重新计算基于一个道具/状态改变一些DOM属性。(实施例:滚动元件的顶部的状态改变之后)

通常情况下,我们使用几个这样的模式中的一次,哦,可以将这些生命周期方法会导致混乱。相关的逻辑是必要的传播跨越几个的这些方法,可以是难以遵循发生的事情在乱哄哄。不过,这并不必须是这样!从根本上说,大多数这些模式可以简化为:做一些事情发生时。而值得庆幸的是,我们有几个挂钩,可以与帮助。

比方说,我们希望在我们的计数器组件摆脱按键的,而是只听文档上点击。而不是写生命周期的方法来安装和拆除的事件处理程序,让我们用一个所谓的新钩useEffect

进口反应,{useState,useEffect}从 “反应”;常量计数器=()=> {常量[CURRENTCOUNT,setCurrentCount] = useState(0);常量incrementCounter =()=> setCurrentCount(CURRENTCOUNT + 1);useEffect(()=> {document.addEventListener( “点击”,incrementCounter);返回()=> {document.removeEventListener( “点击”,incrementCounter);};},[incrementCounter]);常量[isClicking,setIsClicking] = useState(假);常量onmousedown事件=()=> setIsClicking(真);常量onMouseUp =()=> setIsClicking(假);useEffect(()=> {document.addEventListener( “鼠标按下”,onmousedown事件); document.addEventListener( “鼠标松开”,onMouseUp);返回()=> {document.removeEventListener( “鼠标按下”,onmousedown事件); document.removeEventListener(“鼠标松开”,onMouseUp);};},[onmousedown事件,onMouseUp]);返回(
当前计数:{CURRENTCOUNT}是点击:{isClicking} );};

哇,这是一个很大。让我们专注于这些新的一个useEffect块。

useEffect(()=> {document.addEventListener( “点击”,incrementCounter);返回()=> {document.removeEventListener( “点击”,incrementCounter);};});

这整洁的小钩子可以是棘手的理解,它可能是用老式的命名函数(我怀念那些)更容易:

useEffect(功能设置(){document.addEventListener( “点击”,incrementCounter);返回功能TEARDOWN(){document.removeEventListener( “点击”,incrementCounter);};});

这是更好的。从本质上讲,我们告诉组件来运行我们的建立()这之后呈现功能,并利用自身的后清理拆除功能,先下一渲染。例如,如果该部件呈现三次,这将如下运行功能:

  1. 给予
  2. 建立()
  3. 给予(×2)
  4. 拆除()
  5. 建立()
  6. 给予(×3)
  7. 拆除()
  8. 建立()

…等等。然而,为了提高效率起见,我们可以选择通过useEffect第二参数:

useEffect(功能设置(){document.addEventListener( “点击”,incrementCounter);返回功能TEARDOWN(){document.removeEventListener( “点击”,incrementCounter);};},[incrementCounter]);

这第二个参数是项目的列表应该导致组件重新运行建立拆除功能。通常这个列表将包括您在参考外部变量useEffect呼叫在这种情况下,incrementCounter。这让我们避免浪费的设置和拆解。更强大的是,它可以帮助我们防止简单地通过它传递值的空单变化对运行超过一次,效果。有用!

在上面的例子中,我们useEffect设置每次某些文档事件监听器incrementCounterchanges, but we can use this same hook to run any sort of side effect we’d like—from subscribing and unsubscribing to a web socket, hitting an API for updated data when some prop changes, or any other prop or state driven action we might want to take.

它的价值在这一点上指出拆除返回值是完全可选的。我们并不总是有我们想要清理的东西,但有时我们做,现在我们可以保留相关的逻辑在一起。它也可以是一个有益的提醒清除之后,我们的副作用,我们可能以前忘记清理的componentWillUnmount

累了:组件方法

连线:useCallback

“可是等等!”,你可能会约说现在权,“我们在渲染函数中定义incrementCounter!这会在每一个渲染被重新定义,所以不会我们的hook每次运行?”好吧好吧好吧,我不知道你在关注下这个过于拉伸薄例子组件。但你是绝对正确的!

进口反应,{useState,useEffect}从 “反应”;常量计数器=()=> {常量[CURRENTCOUNT,setCurrentCount] = useState(0);常量incrementCounter =()=> setCurrentCount(CURRENTCOUNT + 1);useEffect(()=> {document.addEventListener( “点击”,incrementCounter);返回()=> {document.removeEventListener( “点击”,incrementCounter);};},[incrementCounter]);返回(
当前计数:{CURRENTCOUNT} );};

因为incrementCounter正在被重新定义在每个渲染,在第二个参数使用它useEffect并没有真正给我们带来多少好处。值得庆幸的是,有一个钩的!

进口反应,{useState,useEffect,useCallback}从 “反应”;常量计数器=()=> {常量[CURRENTCOUNT,setCurrentCount] = useState(0);常量incrementCounter = useCallback(()=> setCurrentCount(CURRENTCOUNT + 1),[setCurrentCount,CURRENTCOUNT]);useEffect(()=> {document.addEventListener( “点击”,incrementCounter);返回()=> {document.removeEventListener( “点击”,incrementCounter);};},[incrementCounter]);返回(
当前计数:{CURRENTCOUNT} );};

这是我最喜欢的一个,也是最普遍有用,钩,即使你注销挂钩的整体概念,你会想要保留这个在您的工具带。你给useCallback的函数的第一个参数,并返回它的一个版本memoized,只重新计算每当任何在所述第二参数变化的项目。

这是非常有用的,因为我们都知道通过箭头功能下降的道具是没有布埃诺,因为这样会造成浪费的重新渲染。现在,固定是那么容易,因为在你的包裹箭头功能useCallback钩!这是挤出一些改进的性能,并防止不必要的重新描绘一个简单的方法。

累了:重新选择

连线:useMemo

我想在这一点上失职,如果我没有谈论的近亲useCallback,令人惊讶的有用useMemo钩。使用这个钩子,你发现自己计算渲染块昂贵的东西任何时候。例如,如果你的身体成分看起来是这样的:

进口从“反应”反应;常量MyComponent的=({someObject})=> {常量some​​Number = Object.keys(someObject).MAP((键)=> someObject [值]).filter((值)=>值%2 === 0)。减少((总和,电流)=>总和+电流,0)const的数组= [...新阵列(someNumber)];返回(
{array.map(()=> <跨度/>)} );};

(这是一个可笑的人为的例子,当然,但告诉我着脸你没有一些地方在你的代码库,看起来像这一点,我会吃我的帽子。)你可能反而把这个包昂贵的计算中useMemo像这样:

进口反应,从 “反应”{useMemo};常量MyComponent的=({someObject})=> {常量阵列= useMemo(()=> {常量some​​Number = Object.keys(someObject).MAP((键)=> someObject [值]).filter((值)=>值%2 === 0)。降低((总和,电流)=>总和+电流,0)返回[...新阵列(someNumber)];},[someObject]);返回(
{array.map(()=> <跨度/>)} );};

现在,数组将只要如果值重新计算本身someObject变化。这比重新计算它使每一个(虽然还是公认效率低不是完全删除,因为这是非常糟糕的™️)更有效。在过去,像库重新选择给我们工具,让类似的性能优势,但现在你可以收获这些好处,而不必导入额外的库。

累:高位成分/混入

连线:定制挂钩

还有一件事。回到我们的反例(你以为我会让我们的计数器组件打爆,从来没有!),如果由于某种原因,深不可测,想点击处理程序附加到文档中的第二个组成部分是什么?也许一个上点击生成随机数。

进口反应,{useState,useEffect,useCallback}从 “反应”;常量RandomNumberGenerator =()=> {常量[randomNumber,setRandomNumber] = useState();常量getRandomNumber的= useCallback(()=> setRandomNumber(4),//保证是随机[setRandomNumber]);useEffect(()=> {document.addEventListener( “点击”,getRandomNumber的);返回()=> {document.removeEventListener( “点击”,getRandomNumber的);};},[getRandomNumber的]);返回(
随机数是:{randomNumber} );};

那么,我们可以只重新定义我们的新组件内部的逻辑,但是这没有乐趣。如果我们感到聪明,我们可能会使用一个高阶组件,或渲染道具做到这一点。但是,这是自定义的钩子可以大放异彩。我们可以抽象共享​​逻辑出一个自定义的钩子,我们可以称之为useDocumentClick

进口反应,{useState,useEffect,useCallback}从 “反应”;功能useDocumentClick(onDocumentClick){useEffect(()=> {document.addEventListener( “点击”,onDocumentClick);返回()=> {document.removeEventListener( “点击”,onDocumentClick);};},[onDocumentClick]);}常量计数器=()=> {常量[CURRENTCOUNT,setCurrentCount] = useState(0);常量incrementCounter = useCallback(()=> setCurrentCount(CURRENTCOUNT + 1),[setCurrentCount,CURRENTCOUNT]);useDocumentClick(incrementCounter);返回(
当前计数:{CURRENTCOUNT} );};常量RandomNumberGenerator =()=> {常量[randomNumber,setRandomNumber] = useState();常量getRandomNumber的= useCallback(()=> setRandomNumber(4),//保证是随机[setRandomNumber]);useDocumentClick(getRandomNumber的); return (
Random number is: {randomNumber}
); };

自定义挂钩可以用来就像你其他任何挂钩。您的自定义钩子应该总是以名称,但除此之外,你可以感受到自由地做自己喜欢的任意使用其他挂钩像在那里,包括useStateuseEffect。你永远成分需要知道钩只是其API的实现细节。这可以帮助移动复杂的状态或副作用逻辑移出组件,并使得该逻辑容易重复使用的道路。你不一定要为所有挂钩做到这一点,但它是使组件逻辑可重复使用的,当你需要它是一个更容易的方法。

例如,如果你发现自己从你的组件进行API调用漂亮的时候,你可能会写一个叫做挂钩useAPI

功能useAPI(方法,端点,数据){常量[isLoading,setIsLoading] = useState(假);常量[错误,SETERROR] = useState(NULL);常量[响应,setResponse] = useState(NULL);useEffect(异步()=> {尝试{setIsLoading(真); setResponse(NULL); SETERROR(NULL); const的解析度= AWAIT取(端点,{方法,数据}); setIsLoading(假); setResponse(RES);}赶上(ERR){setIsLoading(假); SETERROR(ERR);}},[方法,端点数据]);返回{响应,错误,isLoading,};}

这样,您有与组件的API谈论一个一致的层。如果您使用的现代技术,如GraphQL和阿波罗一样,我们开始在OkCupid,有一些已经188bet金宝搏官网伟大的开源项目为您提供几个这样的强大的自定义挂钩,以及越来越多的收藏其他各种工具挂钩

一个字的警告⚠️

有你有使用挂钩时,要记住一个重要法则:你的钩子必须以相同的顺序进行声明,每次组件呈现。这也就意味着:挂钩不能来限定内部条件语句,条件返回之后,或者在环。钩必须在压痕的“顶级”来调用。如果这似乎不可思议,这是因为通过使用Javascript标准,这是一个不寻常的限制。这是在语言之上的附加约束,而且必不可少,为了应对能以正确的方式来保存状态。对于为什么这限制存在的更多信息,我建议你阅读丹·阿布拉莫夫的博客文章上反应过度。好消息是:有一个eslint插入为了帮助您防止犯那个错误。


你上瘾了吗?

反应钩真的可以改变我们思考自己的组件中的状态和状态更新,这可能会导致一些真正伟大的重构机会的方式。虽然这是很有诱惑力的“翻译”我们的组件1:从类1到钩,这往往限制了利益挂钩可以提供。我希望本指南帮助皮克在钩的力量你的兴趣,并帮助我们的重构如何看待我们的组件一些常见的模式。使用优化我们现有代码的机会useCallbackuseMemo不能被夸大了,因为他们可以在我们现有的功能部件提供一些简单的性能胜。什么时候它更直观的使用钩子与使用类组件肯定会愤怒,但我认为最起码,钩为我们提供了在我们的工具带一些非常强大的新工具用于表达状态的组件,甚至优化无国籍者的争论。