React Hooks 用法详解2

简介: React Hooks 用法详解

4、useReducer处理更为复杂state结构

语法

const [state, dispatch] = useReducer(reducer, initialArg, init);

useReducer 接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。

我们可以使用 useReducer 来重新写我们开篇计数器的demo:

Example:

import React, { useReducer } from 'react';
const initialState = {count: 0};
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1};
        case 'decrement':
            return {count: state.count - 1};
        default:
            throw new Error();
    }
}
export default () => {
    // 使用 useReducer 函数创建状态 state 以及更新状态的 dispatch 函数
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <>
            Count: {state.count}
            <br />
            <button onClick={() => dispatch({type: 'increment'})}>+</button>
            <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
    );
}

优化:延迟初始化

还可以懒惰地创建初始状态。为此,您可以将init函数作为第三个参数传递。初始状态将设置为 init(initialArg)

它允许您提取用于计算 reducer 外部的初始状态的逻辑。这对于稍后重置状态以响应操作也很方便:

Example.js

import React, { useReducer } from 'react';
function init(initialCount) {
    return {count: initialCount};
}
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1};
        case 'decrement':
            return {count: state.count - 1};
        case 'reset':
            return init(action.payload);
        default:
            throw new Error();
    }
}
export default ({initialCount = 0}) => {
    const [state, dispatch] = useReducer(reducer, initialCount, init);
    return (
        <>
            Count: {state.count}
            <br />
            <button
                onClick={() => dispatch({type: 'reset', payload: initialCount})}>
                Reset
            </button>
            <button onClick={() => dispatch({type: 'increment'})}>+</button>
            <button onClick={() => dispatch({type: 'decrement'})}>-</button>
        </>
    );
}

与 useState 的区别

  • state 状态值结构比较复杂时,使用 useReducer 更有优势。
  • 使用 useState 获取的 setState 方法更新数据时是异步的;而使用 useReducer 获取的 dispatch 方法更新数据是同步的。

针对第二点区别,我们可以演示一下:在上面 useState 用法的例子中,我们新增一个 button

useState 中的 Example.js

import React, { useState } from 'react';
function Example() {
    // 声明一个名为“count”的新状态变量
    const [count, setCount] = useState(0);
    return (
        <div>
            <p>You clicked {count} times</p>
            <button onClick={() => setCount(count + 1)}>
                Click me
            </button>
            <button onClick={() => {
                setCount(count + 1);
                setCount(count + 1);
            }}>
                测试能否连加两次
            </button>
        </div>
    );
}
export default Example;

点击 测试能否连加两次 按钮,会发现,点击一次, count 还是只增加了 1,由此可见,useState 确实是 异步 更新数据;

在上面 useReducer 用法的例子中,我们新增一个 button:useReducer 中的

Example.js

import React, { useReducer } from 'react';
const initialState = {count: 0};
function reducer(state, action) {
    switch (action.type) {
        case 'increment':
            return {count: state.count + 1};
        case 'decrement':
            return {count: state.count - 1};
        default:
            throw new Error();
    }
}
export default () => {
    // 使用 useReducer 函数创建状态 state 以及更新状态的 dispatch 函数
    const [state, dispatch] = useReducer(reducer, initialState);
    return (
        <>
            Count: {state.count}
            <br />
            <button onClick={() => dispatch({type: 'increment'})}>+</button>
            <button onClick={() => dispatch({type: 'decrement'})}>-</button>
            <button onClick={() => {
                dispatch({type: 'increment'});
                dispatch({type: 'increment'});
            }}>
                测试能否连加两次
            </button>
        </>
    );
}


点击 测试能否连加两次 按钮,会发现,点击一次, count 增加了 2,由此可见,每次dispatch 一个 action 就会更新一次数据,useReducer 确实是 同步 更新数据;

对于 useReducer 和 useState的区别主要是以下两点:

  • 当 state 状态值结构比较复杂时,使用 useReducer 更有优势。
  • 使用 useState 获取的 setState 方法更新数据时是异步的;而使用 useReducer 获取的 dispatch 方法更新数据是同步的。

5、useMemo性能优化

语法:

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

返回一个memoized值。传递“创建”函数和依赖项数组。useMemo只会在其中一个依赖项发生更改时重新计算memoized值。此优化有助于避免在每个渲染上进行昂贵的计算。

useMemo在渲染过程中传递的函数会运行。不要做那些在渲染时通常不会做的事情。例如,副作用属于useEffect,而不是useMemo。

用法

useMemo 可以帮助我们优化子组件的渲染,比如这种场景:在 A 组件中有两个子组件 B 和 C,当 A 组件中传给 B 的 props 发生变化时,A 组件状态会改变,重新渲染。此时 B 和 C 也都会重新渲染。其实这种情况是比较浪费资源的,现在我们就可以使用 useMemo 进行优化,B 组件用到的 props 变化时,只有 B 发生改变,而 C 却不会重新渲染。

例子:

ExampleA.js

import React from 'react';
export default ({ text }) => {
    console.log('Example A:', 'render');
    return <div>Example A 组件:{ text }</div>
}

ExampleB.js

import React from 'react';
export default ({ text }) => {
    console.log('Example B:', 'render');
    return <div>Example B 组件:{ text }</div>
}

App.js

import React, { useState } from 'react';
import ExampleA from './ExampleA';
import ExampleB from './ExampleB';
import './App.css';
export default () => {
    const [a, setA] = useState('ExampleA');
    const [b, setB] = useState('ExampleB');
    return (
        <div>
            <ExampleA text={ a } />
            <ExampleB text={ b } />
            <br />
            <button onClick={ () => setA('修改后的 ExampleA') }>修改传给 ExampleA 的属性</button>
            &nbsp;&nbsp;&nbsp;&nbsp;
            <button onClick={ () => setB('修改后的 ExampleB') }>修改传给 ExampleB 的属性</button>
        </div>
    )
}

此时我们点击上面任意一个按钮,都会看到控制台打印了两条输出, A 和 B 组件都会被重新渲染。

现在我们使用 useMemo 进行优化

App.js

import React, { useState, useMemo } from 'react';
import ExampleA from './ExampleA';
import ExampleB from './ExampleB';
import './App.css';
export default () => {
    const [a, setA] = useState('ExampleA');
    const [b, setB] = useState('ExampleB');
+    const exampleA = useMemo(() => <ExampleA />, [a]);
+    const exampleB = useMemo(() => <ExampleB />, [b]);
    return (
        <div>
+            {/* <ExampleA text={ a } />
+            <ExampleB text={ b } /> */}
+            { exampleA }
+            { exampleB }
            <br />
            <button onClick={ () => setA('修改后的 ExampleA') }>修改传给 ExampleA 的属性</button>
            &nbsp;&nbsp;&nbsp;&nbsp;
            <button onClick={ () => setB('修改后的 ExampleB') }>修改传给 ExampleB 的属性</button>
        </div>
    )
}

此时我们点击不同的按钮,控制台都只会打印一条输出,改变 a 或者 b,A 和 B 组件都只有一个会重新渲染。

6、useCallback优化函数式组件性能

语法:

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

返回值 memoizedCallback 是一个 memoized 回调。传递内联回调和一系列依赖项。useCallback将返回一个回忆的memoized版本,该版本仅在其中一个依赖项发生更改时才会更改。当将回调传递给依赖于引用相等性的优化子组件以防止不必要的渲染(例如shouldComponentUpdate)时,这非常有用。

这个 Hook 的 API 不能够一两句解释的清楚,建议看一下这篇文章:useHooks 第一期:聊聊 hooks 中的 useCallback。里面介绍的比较详细。

7、useRef获取dom

语法:

const refContainer = useRef(initialValue);

类组件、React 元素用 React.createRef,如:remindRef: any = React.createRef();通过 this.remindRef.current获取

函数组件使用 useRef,如let globalToolRef: any = useRef(null);通过globalToolRef.current获取

useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传递的参数(initialValue)。返回的对象将存留在整个组件的生命周期中。

  • 从本质上讲,useRef就像一个“盒子”,可以在其.current财产中保持一个可变的价值。
  • useRef() Hooks 不仅适用于 DOM 引用。“ref” 对象是一个通用容器,其 current 属性是可变的,可以保存任何值(可以是元素、对象、基本类型、甚至函数),类似于类上的实例属性。

注意:useRef() 比 ref 属性更有用。与在类中使用 instance(实例) 字段的方式类似,它可以 方便地保留任何可变值。

注意,内容更改时useRef 不会通知您。变异.current属性不会导致重新渲染。如果要在React将引用附加或分离到DOM节点时运行某些代码,则可能需要使用回调引用。

使用

下面这个例子中展示了可以在 useRef() 生成的 refcurrent 中存入元素、字符串

Example.js

import React, { useRef, useState, useEffect } from 'react';
export default () => {
    // 使用 useRef 创建 inputEl
    const inputEl = useRef(null);
    const [text, updateText] = useState('');
    // 使用 useRef 创建 textRef
    const textRef = useRef();
    useEffect(() => {
        // 将 text 值存入 textRef.current 中
        textRef.current = text;
        console.log('textRef.current:', textRef.current);
    });
    const onButtonClick = () => {
        // `current` points to the mounted text input element
        inputEl.current.value = "Hello, useRef";
    };
    return (
        <>
            {/* 保存 input 的 ref 到 inputEl */}
            <input ref={ inputEl } type="text" />
            <button onClick={ onButtonClick }>在 input 上展示文字</button>
            <br />
            <br />
            <input value={text} onChange={e => updateText(e.target.value)} />
        </>
    );
}

点击 在 input 上展示文字 按钮,就可以看到第一个 input 上出现 Hello, useRef;在第二个 input 中输入内容,可以看到控制台打印出对应的内容。


相关文章
|
1月前
|
前端开发 JavaScript 开发者
深入理解React Hooks:提升前端开发效率的关键
【10月更文挑战第5天】深入理解React Hooks:提升前端开发效率的关键
|
1月前
|
前端开发 JavaScript
React Hooks 全面解析
【10月更文挑战第11天】React Hooks 是 React 16.8 引入的新特性,允许在函数组件中使用状态和其他 React 特性,简化了状态管理和生命周期管理。本文从基础概念入手,详细介绍了 `useState` 和 `useEffect` 的用法,探讨了常见问题和易错点,并提供了代码示例。通过学习本文,你将更好地理解和使用 Hooks,提升开发效率。
69 4
|
1月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
1月前
|
前端开发 JavaScript API
探索React Hooks:前端开发的革命性工具
【10月更文挑战第5天】探索React Hooks:前端开发的革命性工具
|
15天前
|
前端开发 JavaScript
深入探索React Hooks:从useState到useEffect
深入探索React Hooks:从useState到useEffect
|
25天前
|
前端开发 JavaScript 开发者
“揭秘React Hooks的神秘面纱:如何掌握这些改变游戏规则的超能力以打造无敌前端应用”
【10月更文挑战第25天】React Hooks 自 2018 年推出以来,已成为 React 功能组件的重要组成部分。本文全面解析了 React Hooks 的核心概念,包括 `useState` 和 `useEffect` 的使用方法,并提供了最佳实践,如避免过度使用 Hooks、保持 Hooks 调用顺序一致、使用 `useReducer` 管理复杂状态逻辑、自定义 Hooks 封装复用逻辑等,帮助开发者更高效地使用 Hooks,构建健壮且易于维护的 React 应用。
30 2
|
30天前
|
前端开发 开发者
React 提供的其他重要 Hooks
【10月更文挑战第20天】React 提供了一系列强大的 Hooks,除了 `useRef` 之外,还有许多其他重要的 Hooks,它们共同构成了函数式组件开发的基础。
35 6
|
7天前
|
缓存 前端开发 开发者
深入理解React Hooks,打造高效响应式UI
深入理解React Hooks,打造高效响应式UI
15 0
|
1月前
|
前端开发 JavaScript 开发者
React Hooks
10月更文挑战第13天
35 1
|
1月前
|
前端开发