React 初探之 Hook

概述

Hook 使你在非 class 的情况下可以使用更多的 React 特性。Hook 是一些可以在函数组件里钩入React state 及生命周期等特性的函数。Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则

  • 只在最顶层使用 Hook。不要在循环,条件或嵌套函数中调用 Hook。
  • 只在 React 函数中调用 Hook。不要在普通的 JavaScript 函数中调用 Hook。可以在
    1. React 的函数组件中调用 Hook
    2. 在自定义 Hook 中调用其他 Hook

Hook 是一种服用状态逻辑的方式,它不复用 state 本身。事实上 Hook 的每次调用都有一个完全独立的 state,因此可以在单个组件中多次调用同一个自定义 Hook。

React 依据 Hook 调用的顺序知道哪个 state 对应哪个 useState

State Hook

在 React 函数组件上添加内部 state

Effect Hook

默认情况下,React 会在每次渲染后调用副作用函数,包括第一次渲染的时候。通过使用 Hook,可以把组件内相关的副作用组织在一起(例如创建订阅及取消订阅),而不是要把他们拆分到不同的生命周期函数里。

  • 副作用 => 数据获取,设置订阅以及手动更改 React 组件中的 DOM 都属于副作用

在 React 组件中有两种常见副作用操作:需要清除的和不需要清除的

无需清除的 effect

在 React 更新 DOM 之后运行一些额外的代码。发送网络请求,手动变更 DOM,记录日志,这些都是常见的无需清除的操作。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

每次重新渲染,都会生成新的 effect,替换掉之前的。effect 更像是渲染结果的一部分,每个 effect 属于一次特定的渲染

需要清除的 effect

订阅外部数据源这种副作用是需要清除的,可以防止引起内存泄漏。副作用函数可以通过返回一个函数来指定如何清除副作用

React 何时清除 effect?React 会在组件卸载的时候执行清除操作 + effect 在每次渲染的时候都会执行 => React 会在执行当前 effect 之前对上一个 effect 进行清除。

自定义 Hook

自定义 Hook 可以在不增加组件的情况下在组件之间重用一些状态逻辑,即可以将组件逻辑提取到可重用的函数中。自定义 Hook 更像是一种约定而不是功能。

  • 自定义 Hook 必须以 use 开头。
  • 在两个组件中使用相同 Hook 不会共享 state。每次使用自定义 Hook 时,其中所有 state 和副作用都是完全隔离的。
  • 每次调用 Hook,自定义 Hook 都会获取独立的 state。直接调用自定义 Hook 时,从 React Dee角度来看,组件只是调用了 useStateuseEffect

常用 Hook

useState

const [state, setState] = useState(initialState);

setState

setState 可以接收

  • 新值,从而更新 state,使得 state 为新值
  • 函数,参数为先前的 statestate 为函数的返回值
    const [count, setCounnt] = useState(0);
    setCounnt(prevCount => prevCount + 1);
    

useState

  • 如果初始 state 需要通过复杂计算获得,则可以传入一个函数,在函数中计算并返回初始的 state,此函数只在初始渲染时被调用
    const [state, setState] = useState(() => {
        const initState = someExpensiveComputation(props);
        return initState;
    });
    

useEffect

useEffect 可以看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。

通过传递数组作为 useEffect 的第二个可选参数从而跳过 Effect 进行性能优化。数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。

useEffect 的 effect 的执行时机

componentDidMountcomponentDidUpdate 不同的是,在浏览器完成布局与绘制之后,传给 useEffect 的函数会延迟调用,所以不应在函数中执行阻塞浏览器更新屏幕的操作

useRef

const redContainer = useRef(initValue);

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initValue)。返回的 ref 对象在组件的整个生命周期内保持不变。

本质上,useRef 就像是可以在其 .current 属性中保存一个可变值的"盒子"。

useRef() Hook 不仅可以用于 DOM refs,「ref」对象是一个 current 属性可变且可以容纳任意值的通用容器

例1

function TextInputWithFocusButton() {
    const inputEle = useRef(null);
    const onButtonClick = () => {
        // current 指向已挂载到 DOM 上的文本输入元素
        inputEle.current.focus();
    }
    return (
        <>
            <input ref={inputEle} type='text' />
            <button onClick={onButtonClick}>Focus the input</button>
        </>
    )
}

例2:useRef 可以很方便的保存任何可变值

function Timer() {
    const intervalRef = useRef();
    useEffect(() => {
        intervalRef.current = setinterval(() => {
            // ...
        });
        
        return () => {
            clearInterval(intervalRef.current);
        }
    });
}

注: 当 ref 对象内容发生变化时,useRef 并不会通知。变更 .current 属性不会引起组件重新渲染。如果想要在 React 绑定和解绑 DOM 节点的 ref 时运行某些代码,则需要使用 useCallback 来实现

useCallback

返回一个 memoized 函数。

const memoizedCallback = useCallback(() => {
    doSomething(a, b);
}, [a, b]);

把内联回调函数及依赖项数组作为参数传入 useCallback,它将返回该回调函数的 memoized 版本,该回调函数仅在某个依赖项改变时才会更新。当把回调函数传递给经过优化的并使用引用相等性去避免非必要渲染(例如 shouldComponentUpdate)的子组件时,它将非常有用。

useCallback(fn, deps) 相当于 useMemo(() => fn, deps)

useMemo

返回一个 memoized 值

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);

把创建函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。

如果没有提供依赖项,useMemo 在每次渲染时都会计算新的值。可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证

注:传入 useMemo 的函数会在渲染期间执行。不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴

注意:

  • useState 不会自动合并并更新对象。可是使用函数式的 setState 达到合并并更新对象的效果
    setState(prevState => ({...prevState, ...updateValues}));
    
  • 如果在渲染期间执行了高开销的计算,则可以使用 useMemo 来进行了优化
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • 自从Hooks出现,函数式组件(Function Component)的功能在不断丰富,你很可能已经运用Hooks...
    tracyXia阅读 11,399评论 2 7
  • 主要介绍 useState useEffect useReducer useContext 用法 你还在为...
    叫我苏轼好吗阅读 27,384评论 3 41
  • Hook是React16.8的新增特性。它可以让你在不编写class的情况下使用state以及其他的react特性...
    逝去丶浅秋阅读 588评论 0 2
  • Hooks是 React v16.8 的新特性,可以在不使用类组件的情况下,使用 state 以及其他的React...
    hellomyshadow阅读 13,418评论 0 5
  • 这是一则随心到信马由缰的动态。 我喜欢有事没事追忆过往。或是翻翻照片、或是从头到尾看一遍写过的文字、或是听听那些年...
    婠执阅读 451评论 1 8