美文网首页
React Hooks

React Hooks

作者: 风雅欢乐 | 来源:发表于2020-05-03 16:32 被阅读0次

State Hook

State Hook是一个在函数组件中使用的函数(useState), 用于在函数组件中使用状态

useState

  • 函数有一个参数, 这个参数的值表示状态的默认值
  • 函数的返回值是一个数组, 该数组一定包含两项
    • 第一项: 当前状态的值
    • 第二项: 改变状态的函数

一个函数组件中可以有多个状态, 这种做法非常有利于横切关注点.

注意的细节

  1. useState最好写到函数组件的起始位置, 便于阅读
  2. useState严禁出现在代码块(判断, 循环)中
  3. useState返回的函数(数组第二项), 引用不变(节约内存空间)
  4. 使用函数改变数据, 若数据和之前的数据完全相等(使用Object.is比较), 不会导致重新渲染, 以达到优化效率的目的
  5. 使用函数改变数据, 传入的值不会和原来的数据进行合并, 而是直接替换
  6. 如果要实现组件强制刷新
    1. 类组件: 使用forceUpdate函数
    2. 函数组件: 使用一个空对象的useState
  7. 如果某些状态之间没有必然的联系, 应该分化为不同的状态, 而不要合并成一个对象
  8. 和类组件的状态一样, 函数组件中改变状态可能是异步的(在DOM事件中), 多个状态变化会合并以提高效率, 此时不能信任之前的状态, 而应该使用回调函数的方式改变状态. 如果状态的变化要用到之前的状态, 尽量传递函数.
  9. 如果想要组件强制刷新, 可以使用一个空对象的useState
const [, foeceUpdate] = useState({});

Effect Hook

Effect Hook: 用于在函数组件中处理副作用

副作用:

  1. ajax请求
  2. 计时器
  3. 其他异步操作
  4. 更改真实的DOM对象
  5. 本地存储
  6. 其他会对外部产生影响的操作

函数: useEffect, 该函数接受一个函数作为参数, 接收的函数就是需要进行副作用操作的函数.

细节

  1. 副作用函数的运行时间点, 是在页面完成真实的UI渲染之后. 因此它的执行是异步的, 并且不会阻塞浏览器
    1. 与类组件中componentDidMount和componentDidUpdate的区别
    2. componentDidMount和componentDidUpdate, 更改了真实DOM, 但是用户还没有看到UI更新, 同步的.
    3. useEffect中的副作用函数, 更改了真实DOM, 并且用户已经看到了UI更新, 异步的.
  2. 每个函数组中, 可以多次使用useEffect, 但不要放入判断或循环等代码块中.
  3. useEffect中的副作用函数, 可以有返回值, 返回值必须是一个函数, 该函数叫做清理函数
    1. 该函数运行时间点, 在每次运行副作用函数之前
    2. 首次渲染组件, 不会运行
    3. 组件被销毁时一定会运行
  4. useEffect函数可以传递第二个参数
    1. 第二个参数是一个数组
    2. 数组中记录该副作用的依赖数据
    3. 当组件重新渲染后, 只有依赖数据与上一次不一样时, 才会执行副作用
    4. 所以, 当传递了依赖数据之后, 如果数据没有发生变化
      • 副作用函数仅在第一次渲染后运行
      • 清理函数仅在卸载组件后运行
  5. 副作用函数中, 如果使用了函数上下文中的变量, 则由于闭包的影响, 会导致副作用函数中的变量不会实时变化.
  6. 副作用函数在每次注册时, 会覆盖掉之前的副作用函数, 因此, 尽量保持副作用函数稳定, 否则控制起来会比较复杂

自定义Hook

自定义Hook: 将一些常用的, 跨越多个组件的Hook功能抽离出去形成一个函数, 该函数就是自定义Hook. 自定义Hook, 由于其内部使用Hook功能, 所以它本身也需要按照Hook的规则实现:

  1. 函数名必须以use开头
  2. 调用自定义Hook函数时, 必须放到代码的最前面
import { useEffect, useState } from 'react';
import { getAllStudents } from '../services/student';

export default function useAllStudents() {
    const [students, setStudents] = useState([]);
    useEffect(() => {
        (async function () {
            const stus = await getAllStudents();
            setStudents(stus);
        })();
    }, []);
    return students;
}

// 应用
import React from 'react'
import useAllStudents from './myHooks/useAllStudents';

function Test() {
    const stus = useAllStudents();
    const list = stus.map(it => {
        return <li key={it.id}>{it.name}</li>
    });
    return (
        <ul>
            {list}
        </ul>
    )
}

export default function App() {
    return (
        <div>
            <Test />
        </div>
    );
}

Reducer Hook

通用的useReducer函数(实际上官方已经有useReduce实现)

import { useState } from 'react';

/**
 * 通用的useReducer函数
 * @param {function} reducer reducer函数, 标准格式
 * @param {any} initialState 初始状态
 * @param {function} func 第三个参数是个函数, 计算初始值. 如果有第三个参数, 则第二个参数会当成第三个函数参数的参数传入
 */
export default function useReducer(reducer, initialState, func) {
    const [state, setState] = useState(func ? func(initialState) : initialState);

    function dispatch(action) {
        const newState = reducer(state, action);
        setState(newState);
    }

    return [state, dispatch];
}

Context Hook

用于获取上下文数据

import React, { useContent } from 'react';

const ctx = React.createContext();

export default function App() {
    return <div>
        <ctx.Provider value="abc">
            <Test />
        </ctx.Provider>
    </div>
}

// 以前的使用方式
function Test() {
    return <ctx.Consumer>
        {
            value => {
                return <h1>Test, 上下文的值: {value}</h1>
            }
        }
    </ctx.Consumer>
}

// 现在的使用方式
function Test() {
    const value = useContent(ctx);
    return <h1>Test, 上下文的值: {value}</h1>
}

Callback Hook

函数名: useCallback

用于得到一个固定引用值的函数, 通常用它进行性能优化

useCallback

该函数有两个参数:

  1. 函数, useCallback会固定该函数的引用, 只要依赖项没有发生变化, 则始终返回之前函数的地址
  2. 数组, 记录依赖项

该函数返回: 引用相对固定的函数地址

import React, { useCallback } from 'react';

class Test extends React.PureComponent {
    render() {
        return <div>
            <h1>{this.props.text}</h1>
            <button onClick={this.props.onClick}>改变文本</button>
        </div>
    }
}

export default function App() {
    const [txt, setTxt] = useState(123);
    const handleClick = useCallback(() => {
        setTxt(Math.random());
    }, []);

    return (
        <div>
            {/* 函数的地址每次渲染都发生变化, 导致了子组件跟着重新渲染. 若子组件是经过优化的, 则子组件的优化可能就失效 */}
            {/* <Test text={txt} onClick={() => {
                setTxt(Math.random());
            }} /> */}
            <Test text={txt} onClick={handleClick} />
        </div>
    );
}

Memo Hook

用于保持那些比较稳定的数据, 通常用于性能优化.

它和useCallback的区别是:

  1. useCallback必须传入一个函数, 它固定的就是这个传入的函数
  2. useMemo传入一个函数, 但是它固定的是这个函数的返回值, 可以是任意类型

如果React元素本身没有发生变化, 它一定不会重新渲染

import React, { useCallback, useMemo } from 'react';


// 和useCallback的区别
const handleClick = useCallback(() => {
    setTxt(Math.random());
}, []);

const fixedData = useMemo(() => {
    // 函数的返回结果, 给固定下来, 可以返回任何东西
    return () => { console.log('xxx') }
}, []);


// 使用
export default function App() {
    const [range, setRange] = useState({ min: 1, max: 10000 });
    const [n, setN] = useState(0);
    const list = useMemo(() => {
        const lis = [];
        for (let i = range.min; i <= range.max; i++) {
            console.log(i);
            lis.push(<li key={i}>{i}</li>);
        }
        return lis;
    }, [range.min, range.max]);

    return (
        <div>
            <ul>
                {list}
            </ul>
            <input type="number"
                value={n}
                onChange={e => {
                    setN(parseInt(e.target.value));
                }} />
        </div>
    );
}

Ref Hook

useRef函数

  1. 一个参数, 默认值
  2. 返回一个固定的对象, {current: 值}
import React, { useRef } from 'react';

export default function App() {
    // 以前的ref创建和使用方式
    // 每次App组件刷新, 都会重新生成一个新的ref
    const inpRef = React.createRef();

    // 新的useRef方式
    const newRef = useRef();

    return (
        <div>
            <input type="text" ref={inpRef} />
            <button onClick={() => {
                console.log(inpRef.current.value);
            }}>得到input的值</button>
        </div>
    );
}

注意

useRef(initialData) 也可以用来在函数组件中固定数据/对象, 即函数组件多次刷新渲染, 不会导致该对象重新创建, 而是使用和第一次创建时相同的引用的同一个对象!!!

ImperativeHandle Hook

函数: useImperativeHandle

  1. 参数1: ref, 函数组件在forwardRef后, 传入的第二个参数ref
  2. 参数2: function, 如果不给依赖项, 函数组件每次运行都会调用这个函数, 它的返回结果将附着在ref.current属性上
  3. 参数3: 数组, 依赖项
function Test(props, ref) {
    useImperativeHandle(
        ref,
        () => {
            // 如果不给依赖项, 则每次运行函数组件都会调用
            // 如果使用了依赖项, 则第一次调用后会进行缓存, 只有依赖项发生变化时才会调用
            // 函数的返回值, 附着到ref的current属性上
            return {
                method() {
                    console.log('Test Component called');
                }
            }
        },
        []
    )
    return <h1>Test Component</h1>
}

const TestWrapper = React.forwardRef(Test);

export default function App() {
    const testRef = useRef();
    return (
        <div>
            <TestWrapper ref={testRef} />
            <button onClick={() => {
                testRef.current.method();
            }}>点击调用Test组件的method方法</button>
        </div>
    );
}

LayoutEffect Hook

useEffect: 浏览器渲染完成后, 用户看到效果之后
useLayoutEffect: 完成了DOM改动, 但浏览器还没有重新渲染呈现给用户

应该尽量使用useEffe, 因为它不会导致渲染阻塞(因为浏览器已经完成了渲染, 而如果使用useLayoutEffect并且处理内容非常多, 会阻塞浏览器渲染). 如果出现问题, 再考虑使用useLayoutEffect.

QQ浏览器截图20200503163021.png

DebugValue Hook

useDebugValue: 用于将自定义hook的关联数据显示到调试栏

如果创建的自定义Hook通用性比较高, 可以选择使用useDebugValue方便调试.

import React, { useDebugValue, useState } from 'react';

function useTest() {
    const [students, ] = useState([]);
    // 传入的值会在调试工具栏中的hooks处显示
    useDebugValue(students);
    return students;
}

export default function App() {
    useState(0);
    useState('abc');
    useTest();
    useEffect(() => {
        console.log("effect");
        return () => {
            
        }
    }, [])

    return (
        <div></div>
    );
}
QQ浏览器截图20200503165218.png

相关文章

  • React Hooks

    React Hooks Hooks其实就是有状态的函数式组件。 React Hooks让React的成本降低了很多...

  • react-hooks

    前置 学习面试视频 总结react hooks react-hooks react-hooks为函数组件提供了一些...

  • React Hooks

    前言 React Conf 2018 上 React 提出了关于 React Hooks 的提案,Hooks 作为...

  • 5分钟简单了解React-Hooks

    首先附上官网正文?:React Hooks Hooks are a new addition in React 1...

  • react-hooks

    react-hooks react-hooks 是react16.8以后,react新增的钩子API,目的是增加代...

  • React-hooks API介绍

    react-hooks HOOKS hooks概念在React Conf 2018被提出来,并将在未来的版本中被...

  • React Hooks 入门

    React Hooks 是 React v16.8 版本引入了全新的 API。 React Hooks 基本概念 ...

  • react hooks 源码分析 --- useState

    1. react hooks简介 react hooks 是react 16.8.0 的新增特性,它可以让你在不编...

  • React Hooks的入门简介

    什么是React Hooks? 首先React Hooks是React生态圈里的新特性,它改变了传统react的开...

  • React hooks(钩子)

    React hooks(钩子) React hooks 是React 16.8中的新增功能。它们使您无需编写类即可...

网友评论

      本文标题:React Hooks

      本文链接:https://www.haomeiwen.com/subject/ptekghtx.html