IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> 每天一道面试题(15) - 常用的hooks有哪些 具体说一下 -> 正文阅读

[JavaScript知识库]每天一道面试题(15) - 常用的hooks有哪些 具体说一下

说一下常用的hooks?

这道题是一道开放题, 只能说出来名字只是最初级的答案.
能具体说出每个hook的使用场景并结合有关联的hook进行对比回答才算是一个合格的答案

useState

useState的异步问题

react hooks和类组件一样都有setState的异步问题
在react内部实现中, 维护了一个 isBatchingUpdate变量, 来判断是否批量更新; 总的来说, 在react hooks中最大的不同是

在类组件中可以通过setTimeout来同步获取到更新后的值, 但是在hooks中无法获取到; 只能通过useEffect做一些副作用处理

具体解释可以参考这一篇文章, 写的比较详细

从react原理角度理解setState究竟是同步还是异步以及react hooks中的更新机制

useState为什么返回是一个数组而不是对象

在解构赋值中, 数组的解构赋值是这样的

const foo = ['one', 'two', 'three'];
const [red, yellow, green] = foo;
console.log(red); // "one"
console.log(yellow); // "two"
console.log(green); // "three"

只要保持位置一一对应即可

但是由于对象中的内容是没有顺序的, 所以对象无法通过顺序对应来进行解构赋值, 所以只能通过键名

const user = {
    id: 42,
    is_verified: true
};

const { id, is_verified } = user;

console.log(id); // 42
console.log(is_verified); // true 

所以useState使用数组进行解构赋值, 可以对变量进行命名, 只需要保持顺序对应好即可;

但是如果使用对象的话, 只能使用固定的名字, 而我们通常需要使用多个state, 这样就需要对新的state重新命名, 提高了复杂度

但是其实使用array也会有对应的问题

  • 强顺序, 第一个参数为state, 第二个参数为setState
  • 如果只想使用第二个参数, 必须要这样
const [,setState] = useState()
  • 返回的参数不能太多, 不然会变复杂

useEffect

useEffect用法

useEffect有两个参数, 第一个参数是回调函数, 第二个参数是一个数组作为依赖项;

  • 如果第二个参数不传, 则每次组件更新(渲染)都会执行一遍
  • 如果第二个参数传一个空数组, 只有在首次渲染时执行, 类似于componentDidmount, 但是其实执行时机是不一样的
  • 如果第二个参数里面有依赖项, 则在首次和依赖项改变时会执行(注意!首次也会执行)
  • 如果在回调函数中return一个函数, 组件每次重新渲染的时候都会先执行该函数再调用回调函数

useEffect && useLayoutEffect的区别

useEffect和useLayoutEffect的执行时机不一样

  • useEffect 在渲染时是异步执行,并且要等到浏览器将所有变化渲染到屏幕后才会被执行。
  • useLayoutEffect 在渲染时是同步执行,其执行时机与 componentDidMount,componentDidUpdate 一致, 会阻塞浏览器渲染

useReducer

useReducer 接受一个 reducer 函数作为参数,reducer 接受两个参数一个是 state 另一个是 action 。然后返回一个状态 count 和 dispath,count 是返回状态中的值,而 dispatch 是一个可以发布事件来更新 state 的

function TestReducer() {
    const reducer = (state, action) => {
        switch (action) {
            case 'add':
                return state + 1
            case 'double':
                return state * 2
        }
    }
    const [count, dispatch] = useReducer(reducer, 0)

    return (
        <div>
            {count}
            <button onClick={() => dispatch('add')}>点击添加</button>
            <button onClick={() => dispatch('double')}>点击*2</button>
        </div>
    )
}

用useReducer实现一个useState

所以, useReduceruseState的加强版, 更可以用useReducer来实现一个useState

下面是笔者的简易实现

function MyUseState(initV) {
    const reducer = (state, action) => {
        return action
    }
    const [myState, setMyState] = useReducer(reducer, initV)
    return [myState, setMyState]
}

function TestUseStateUseReducer() {
    const [age, setAge] = MyUseState(50)
    return (
        <div>
            {age}
            <button onClick={() => setAge(18)}>点击年龄变18</button>
        </div>
    )
}

useReducer+useContext实现状态管理

使用useReducer+useContext可以实现类似redux的状态管理, 下一篇文章详细讲述

useMemo && useCallback

在react hooks性能优化中, 这两个hooks必不可少.

useMemo和useCallback接收的参数是一样的,都是在其依赖项发生变化后才执行,都是返回缓存的值

区别在于useMemo返回的是函数运行的结果useCallback返回的是函数

使用场景:
某些函数或者函数运行的结果 要作为参数从父组件传递给子组件的时候, 如果每次父组件更新, 这些值都会重新计算传递给子组件, 那么对于子组件来说就是一个崭新的参数, 就会触发子组件的渲染; 但是好多情况下, 子组件的这个渲染是没有必要的

所以, 使用这两个hooks可以缓存函数或函数的计算结果, 减少子组件的非必要渲染, 从而节省资源

useRef

useRef 通常被用来获取dom对象, 但不建议过多使用ref

另一种用法就是用来保存数据, react文档中有这样一句话: 当 ref 对象内容发生变化时,useRef 并不会通知你。变更 .current 属性不会引发组件重新渲染。

看这个经典的例子

function Counter() {
    const [count, setCount] = useState(0)
    const prevCountRef = useRef()

    useEffect(() => {
        console.log('useEffect-----', count)
        prevCountRef.current = count
    })

    const addCount = () => {
        setCount(count + 1)
    }

    return (
        <div>
            <button onClick={addCount}>click add count</button>
            Now:{count},before:{prevCountRef.current}
        </div>
    )
}
  • 点击按钮之后, count变化(+1)
  • count变化触发组件更新重新渲染(页面更新了)
  • 组件更新之后, 执行useEffect, 此时useEffect中拿到是这一次更新后的值, 但是界面上显示的是上次的值(下次更新显示的是这一次的prevCountRef.current)
    由于ref的变化不会触发组件重新渲染, 所以此时ref.current已经变化了(内存中), 只是没有在页面上显示而已

usePrevious(使用useRef实现usePrevious)

function usePrevious(value) {
    console.log(value)
    const ref = useRef()
    useEffect(() => {
        console.log('in useEffect')
        ref.current = value
    })
    console.log('ref:', ref.current)
    return ref.current
}
  • usePrevious 被调用
  • 先执行useEffect下面的代码, 打印出refreturn ref.current(上一次的值, 这次传进来的下一步才会更新)
  • 再执行useEffect的回调函数, 更新ref.current=value(此时值才会更新掉)

所以return的是上一步传进来的值, 即实现usePrevious功能

function UsePreviousTest() {
    const [count, setCount] = useState(0)
    const prevCount = usePrevious(count)
    const addCount = () => {
        setCount(count + 1)
    }
    return (
        <div>
            <button onClick={addCount}>click to add count</button>
            count:{count}
            prev:{prevCount}
        </div>
    )
}

像上面这样调用就可以拿到上一步的count


PS:

上周去北京出差了, 就没更新文章. (借口)

就是发现每天写一篇文章真的做不到啊

好难啊

还是每周更新两篇吧, 应该不太难, 嗯, 就这么愉快的决定了!

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-28 12:18:13  更:2021-10-28 12:19:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/24 12:35:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码