React进阶 —— Hook学习笔记

1. Hook 简介

Hook 是 React 16.8.0 版本增加的新特性,可以在函数组件中使用 state 以及其他的 React 特性。

✌️Hook 使用规则:

Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:

  • 只能在函数最外层调用 Hook。不要在循环条件判断或者子函数中调用。
  • 只能在 React 的函数组件中调用 Hook,不要在其他 JavaScript 函数中调用。(还有一个地方可以调用 Hook —— 自定义 Hook

下面介绍几个常用的 Hook。

2. useState

useState让函数组件也可以有state状态,并进行状态数据的读写操作。

const [xxx, setXxx] = useState(initValue); // 解构赋值

📐useState() 方法

参数

第一次初始化指定的值在内部作缓存。可以按照需要使用数字字符串对其进行赋值,而不一定是对象

如果想要在state中存储两个不同的变量,只需调用 useState() 两次即可。

返回值

包含 2 个元素的数组,第 1 个为内部当前状态值,第 2 个为更新状态值的函数,一般直接采用解构赋值获取。

📐setXxx() 的写法

setXxx(newValue):参数为非函数值,直接指定新的状态值,内部用其覆盖原来的状态值

setXxx(value => newValue):参数为函数接收原本的状态值,返回新的状态值,内部用其覆盖原来的状态值。

📐完整示例

const App = () => {
    const [count, setCount] = useState(0);

    const add = () => {
        // 第一种写法
        // setCount(count + 1);
        // 第二种写法
        setCount(count => count + 1);
    };

    return (
        <Fragment>
            <h2>当前求和为:{count}</h2>
            <button onClick={add}>点我+1</button>
        </Fragment>
    );
};

声明了一个叫 count 的 state 变量,然后把它设为 0。React 会在重复渲染时记住它当前的值,并且提供最新的值给我们的函数。我们可以通过调用 setCount 来更新当前的 count

在函数中,我们可以直接用 count

<h2>当前求和为:{count}</h2>

更新state

setCount(count + 1);
setCount(count => count + 1);

📐使用多个 state 变量

// 声明多个 state 变量
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: '学习 Hook' }]);

📌不是必须要使用多个state变量,仍然可以将相关数据分为一组。但是,不像 class 中的 this.setStateuseState中更新state变量是替换。不是合并

3. useEffect

useEffect可以在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)。

React 中的副作用操作

  • ajax 请求数据获取
  • 设置订阅 / 启动定时器
  • 手动更改真实 DOM

📐使用规则

useEffect(() => {
    // 在此可以执行任何带副作用操作
    // 相当于componentDidMount()
    return () => {
        // 在组件卸载前执行
        // 在此做一些收尾工作, 比如清除定时器/取消订阅等
        // 相当于componentWillUnmount()
    };
}, [stateValue]); // 监听stateValue
// 如果省略数组,则检测所有的状态,状态有更新就又调用一次回调函数
// 如果指定的是[], 回调函数只会在第一次render()后执行一次

可以把 useEffect 看做如下三个函数的组合:

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

📐每次更新的时候都运行 Effect

// ...
useEffect(() => {
    // ...
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
        ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
});

调用一个新的 effect 之前会对前一个 effect 进行清理。下面按时间列出一个可能会产生的订阅和取消订阅操作调用序列:

// Mount with { friend: { id: 100 } } props
ChatAPI.subscribeToFriendStatus(100, handleStatusChange);     // 运行第一个 effect

// Update with { friend: { id: 200 } } props
ChatAPI.unsubscribeFromFriendStatus(100, handleStatusChange); // 清除上一个 effect
ChatAPI.subscribeToFriendStatus(200, handleStatusChange);     // 运行下一个 effect

// Update with { friend: { id: 300 } } props
ChatAPI.unsubscribeFromFriendStatus(200, handleStatusChange); // 清除上一个 effect
ChatAPI.subscribeToFriendStatus(300, handleStatusChange);     // 运行下一个 effect

// Unmount
ChatAPI.unsubscribeFromFriendStatus(300, handleStatusChange); // 清除最后一个 effect

📐通过跳过 Effect 进行性能优化

如果某些特定值在两次重渲染之间没有发生变化,可以通知 React 跳过对 effect 的调用,只要传递数组作为 useEffect第二个可选参数即可:

useEffect(() => {
    document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新

如果 count 的值是 5,而且组件重渲染的时候 count 还是等于 5,React 将对前一次渲染的 [5] 和后一次渲染的 [5] 进行比较。因为数组中的所有元素都是相等的(5 === 5),React 会跳过这个 effect,这就实现了性能的优化。

当渲染时,如果 count 的值更新成了 6,React 将会把前一次渲染时的数组 [5] 和这次渲染的数组 [6] 中的元素进行对比。这次因为 5 !== 6,React 就会再次调用 effect。

📌如果数组中有多个元素,即使只有一个元素发生变化,React 也会执行 effect。

对于有清除操作的 effect 同样适用:

useEffect(() => {
    function handleStatusChange(status) {
        setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
        ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
}, [props.friend.id]); // 仅在 props.friend.id 发生变化时,重新订阅

📐使用多个 Effect 实现关注点分离

function FriendStatusWithCounter(props) {
    const [count, setCount] = useState(0);
    useEffect(() => {
        document.title = `You clicked ${count} times`;
    });

    const [isOnline, setIsOnline] = useState(null);
    useEffect(() => {
        function handleStatusChange(status) {
            setIsOnline(status.isOnline);
        }

        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
        return () => {
            ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
        };
    });
    // ...
}

📐完整示例

import React, { useState, Fragment, useEffect } from 'react';
import ReactDOM from 'react-dom';

const App = () => {
    const [count, setCount] = useState(0);

    useEffect(() => {
        let timer = setInterval(() => {
            setCount(count => count + 1);
        }, 500);
        console.log('@@@@');
        return () => {
            clearInterval(timer);
        };
    }, [count]);
    // 检测count的变化,每次变化,都会输出'@@@@'
    // 如果是[],则只会输出一次'@@@@'

    const add = () => {
        setCount(count => count + 1);
    };

    const unmount = () => {
        ReactDOM.unmountComponentAtNode(document.getElementById('root'));
    };

    return (
        <Fragment>
            <h2>当前求和为:{count}</h2>
            <button onClick={add}>点我+1</button>
            <button onClick={unmount}>卸载组件</button>
        </Fragment>
    );
};

export default App;

4. useRef

useRef可以在函数组件中存储 / 查找组件内的标签或任意其它数据。保存标签对象,功能与 React.createRef() 一样。

const refContainer = useRef(initialValue);

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

📌当 ref 对象内容发生变化时,useRef不会通知你。变更 .current 属性不会引发组件重新渲染。

import React, { Fragment, useRef } from 'react';

const Demo = () => {
    const myRef = useRef();

    //提示输入的回调
    function show() {
        console.log(myRef.current.value);
    }

    return (
        <Fragment>
            <input type="text" ref={myRef} />
            <button onClick={show}>点击显示值</button>
        </Fragment>
    );
};

export default Demo;

5. Hook 规则

只在最顶层使用 Hook

不要在循环,条件或嵌套函数中调用 Hook ,在 React 函数的最顶层调用 Hook。

如果想要有条件地执行一个 effect,可以将判断放到 Hook 的内部

useEffect(function persistForm() {
    // 👍 将条件判断放置在 effect 中
    if (name !== '') {
        localStorage.setItem('formData', name);
    }
});

只在 React 函数中调用 Hook

不要在普通的 JavaScript 函数中调用 Hook。可以:

  • 在 React 的函数组件中调用 Hook
  • 在自定义 Hook 中调用其他 Hook

📘📘欢迎在我的博客上访问:
https://lzxjack.top/

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火星飞鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值