Hook 是react 16.8新增的特性。它可以让你在不编写Class的情况下使用state以及其他的React特性。
1.HOOKS是什么?->为了拥抱函数式编程。
2.Hooks带来的变革,让函数组件有了状态和其他的react特性,可以替代class。
我们愈发提倡写一些无状态组件,但是正是因为如此,我们在某些情况下必须使用到state来维护我们组件的数据,诸如我们需要去从某些后端接口获取回的列表数据、但是我们为了维护方便吧我们的项目拆分更详尽、粒度更小,此时我们最外层最大的组件如果是函数组件的话就无法在react理念中把数据存到组件上。正是这种具体的需求促使Hook的产 生。
import React, { useState } from 'react'; function Example() { // 声明一个新的叫做 “count” 的 state 变量 const [count, setCount] = useState(0); // useState函数返回一个数组->[state,set方法自定义名字 xxx也可以] (初始值initValue) return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
这里我们引用官网的基本事例来动态的呈现一个最简单的计数器需求。
这里我们需要注意:react是没有计划让hook+function完全代替class component,没有计划移除class,朋友们还是请放心
HOOK解决了什么问题
1、在组件之间复用状态逻辑很难
React没有提供可复用性行为“附加”到组建的途径(例如,把组件连接到store)。如果你使用过React一段时间,你也许会熟悉一些解决此类问题的方案,比如 render props 和 告诫组件。
export default Wrap = (Com) => (props) => { const [lang,setLang] = useState('zh') useEffect(() => { if( getCookie('lang') === 'en' ){ setLang('en') } },[]); return ( <IntlProvider locale={lang} messages={lang==='en'?en_US:zh_CN}> <Com {...props}/> </IntlProvider> ); };
例如,我们借鉴一段可能会碰到的一种国际化的需求。我们不想去每个页面都做一次,可以封装一个高阶组件出来。
但这是一个比较明朗的高阶组件,如果需求更多,可能会很麻烦,自己封装的代码可能使你的代码难以理解。如果你在React DevTOOLS中观察过React应用,你会发现provider consumers highComponent render props等其他抽象层组成的组件形成“嵌套地狱”。尽管我们可以在DevTools过滤掉它们,但这说明了一个更深层次的问题:React需要为共享逻辑提供更好的原生途径。
你可以在使用Hook从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用,Hook使你在无需求改组件结构的情况下复用状态逻辑,这是的组件间或社区内共享Hook变得更便捷
复杂组件变得难以理解
我们经常维护一些组件,组件起初很简单,但是逐渐会被状态逻辑和副作用充斥。每个生命周期常常包含一些不相关的逻辑。例如,组件常常在componentDidMount和componentDidUpdate中获取数据。但是,同一个componentDidMount中可能也包含很多其它的逻辑,如设置事件监听,而之后需在componentWillUnmount中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生bug,并且导致逻辑不一致。在多数情况下,不可能将组件拆分为更小的粒度,因为状态逻辑无处不在。这也给测试带来了一定挑战。同时,这也是很多人将React与状态管理库结合使用的原因之一。但是,这往往会引入了很多抽象概念,需要你在不同的文件之间来回切换,使得复用变得更加困难。为了解决这个问题,Hook将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。你还可以使用reducer来管理组件的内部状态,使其更加可预测。我们将在使用EffectHook中对此展开更多讨论。难以理解的class除了代码复用和代码管理会遇到困难外,我们还发现class是学习React的一大屏障。你必须去理解JavaScript中this的工作方式,这与其他语言存在巨大差异。还不能忘记绑定事件处理器。没有稳定的语法提案,这些代码非常冗余。大家可以很好地理解props,state和自顶向下的数据流,但对class却一筹莫展。即便在有经验的React开发者之间,对于函数组件与class组件的差异也存在分歧,甚至还要区分两种组件的使用场景。另外,React已经发布五年了,我们希望它能在下一个五年也与时俱进。就像Svelte,Angular,Glimmer等其它的库展示的那样,组件预编译会带来巨大的潜力。尤其是在它不局限于模板的时候。最近,我们一直在使用Prepack来试验componentfolding,也取得了初步成效。但是我们发现使用
class组件会无意中鼓励开发者使用一些让优化措施无效的方案。class也给目前的工具带来了一些问题。例如,class不能很好的压缩,并且会使热重载出现不稳定的情况。因此,我们想提供一个使代码
更易于优化的API。
为了解决这些问题,Hook使你在非class的情况下可以使用更多的React特性。从概念上讲,React组件一直更像是函数。而Hook则拥抱了函数,同时也没有牺牲React的精神原则。Hook提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术。
使用Effect HOOK
此处执行一些副作用。 --> 数据获取,设置订阅以及手动更改React组件中的DOM都属于副作用,不管你知不知道这些操作,或副作用这个名字,应该都在组件中使用过他们。
importReact,{useState,useEffect} from "react"; exportdefaultfunctionHookPage(props){ //声明⼀一个叫“count”的state变量量,初始化为0 const [count, setCount] = useState(0); //与componentDidMount和componentDidUpdate相似 useEffect(() => { //更更新title document.title = `Youclicked${count}times`; }); return ( <div> <h3>HookPage</h3> <p>{count}</p> <buttonn onClick={() => setCount(count + 1)}>add</button> </div> ); }
在函数组件主体内(咋合理指在React渲染阶段)改变dom、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名奇妙的bug并破坏UI的一致性。
使用useEffect完成副作用操作。赋值给useEffect的函数会在组件渲染到屏幕之后执行。你可以把effect看作从React的纯函数式世界通往命令式世界的逃生通道。
默认情况下,effect会在每轮渲染结束后执行,但你可以选择让他只在某些值改变的时候才执行。(第二个参数数组的使用,[xxcount])
effect的条件执⾏
默认情况下,effect会在每轮组件渲染完成后执⾏。这样的话,⼀旦effect的依赖发⽣变化,它就会被
重新创建。
然而,在某些场景下这么做可能会矫枉过正。⽐比如,在上⼀章节的订阅示例例中,我们不需要在每次组件
更新时都创建新的订阅,⽽而是仅需要在sourceprops改变时重新创建。
要实现这⼀点,可以给useEffect传递第⼆个参数,它是effect所依赖的值数组。更新后的示例例如
下:
import React, { useState, useEffect } from"react"; export default functionHookPage(props){ //声明⼀一个叫“count”的state变量量,初始化为0 const [count, setCount] = useState(0); const [date, setDate] = useState(newDate()); //与componentDidMount和componentDidUpdate相似 useEffect(() => { //更更新title document.title = `Youclicked${count}times`; }, [count]); useEffect(() => { consttimer = setInterval(() => { setDate(newDate()); }, 1000); }, []); return ( <div> <h3>HookPage</h3> <p>{count}</p> <buttononClick={() => setCount(count + 1)}>add</button> <p>{date.toLocaleTimeString()}</p> </div > ); }
清除Effect
通常,组件卸载时需要清楚effect创建的诸如订阅或计数器ID等资源。要实现这一点,useEfeect函数需返回一个清除函数,以防内存泄露,清除函数会在组件卸载前执行
useEffect(()=>{ consttimer=setInterval(()=>{ setDate(newDate()); },1000); return()=>clearInterval(timer); },[]);