首页 > 其他分享 >react经典面试题解析--持续更新--day02

react经典面试题解析--持续更新--day02

时间:2023-06-20 11:24:39浏览次数:54  
标签:count 闭包 面试题 -- day02 React useState 组件 useEffect

二十一、高阶组件的使用场景

1、数据获取:高阶组件可以在组件挂载时自动获取数据,并将数据通过 props 传递给被包装组件。
2、权限控制:高阶组件可以检查用户是否有访问该组件的权限,从而决定是否渲染该组件。
3、代码重用:高阶组件可以通过封装一些常见的逻辑,来提高代码的复用性。
4、状态管理:高阶组件可以管理一些状态,并通过 props 将状态传递给被包装组件,如redux里的connect。
5、表单处理:高阶组件可以处理表单的数据,包括数据验证、数据提交等,antd里面的Form。
6、设计模式:高阶组件可以作为设计模式的一部分,例如实现观察者模式、策略模式等

二十二、为什么会出现hooks

class component 学习成本高

我们在class component中要学习生命周期,React15、React16.3、React16.4到React17生命周期有了很多变化。生命周期在class组件中非常重要。但是太多的太多的生命周期难记,有些也不知道具体的场景麻烦。还有就是this指向的问题比如我们要写大量的bind函数来改变this的指向,当然也可以通过装饰器等其他方法改变,但是本质上来说还是要跟this打交道

class component 逻辑代码分散

我们在学习代码的第一天,就应该知道高内聚、低耦合这六字箴言。设计组件也是一样的,我们应当考虑代码的高可复用性。然而在class组件中,我们实现一个功能,就不得不把相同业务的一些逻辑分散到各个生命周期中,就显得逻辑分散,比如我们设置一个定时器,就要考虑在合适的生命周期里面初始化定时器,以及销毁定时器等显的逻辑很分散

react hooks 逻辑复用更加便捷

Class组件逻辑复用一般使用的都是HOC和Render Props。但这两者虽然都可以实现逻辑复用,但是并没有让组件和代码变得好用和优美起来,这二者都会存在的一个问题是,逻辑的复用会导致嵌套层级过深,形成嵌套地狱。使用class组件,表单组件、国际化、Redux等混在一块形成一长串的高阶组件的形式,就很恶心

二十三、useState的实现原理

React 16.8.0 正式增加了 Hooks ,它为函数组件引入了 state 的能力,换句话说就是使函数组件拥有了 Class 组件的功能。
React.useState() 返回的第二个参数 setState 用于更新 state ,并且会触发新的渲染。同时,在后续新的渲染中 React.useState() 返回的第一个 state 值始终是最新的。
为了保证 memoizedState 的顺序与 React.useState() 正确对应,我们需要保证 Hooks 在最顶层调用,也就是不能在循环、条件或嵌套函数中调用。
React.useState() 通过 Object.is() 来判断 memoizedState 是否需要更新

二十四、useEffect模仿生命周期

import React, { useState, useEffect } from 'react';
export default function hook() {

  const [num, setNum] = useState(1)

  /**
   * 第一个参数是回调函数
   * 第二个参数是依赖项
   * 每次num变化时都会变化
   * 
   * 注意初始化的时候,也会调用一次
   */
  useEffect(() => {
    console.log("每次num,改变我才会触发")
    
    return () => {
      /**
       * 这是卸载的回调可以执行某些操作
       * 如果不想执行,则可以什么都不写
       */
      console.log("卸载当前监听")
    }
  }, [num])

  useEffect(() => {
    console.log("每次render页面我就会触发")
    return () => {
      console.log("卸载当前监听")
    }
  })

  return (
    <div>
      <button onClick={() => setNum(num + 1)}>+1</button>
      <div>你好,react hook{num}</div>
    </div>
  );
}

二十五、什么是不可变数据

在编程领域,Immutable Data 是指一种一旦创建就不能更改的数据结构。它的理念是:在赋值时,产生一个与原对象完全一样的新对象,指向不同的内存地址,互不影响

二十六、为什么 React 需要 Immutable Data

调用setState时,React 会以 shallowMerge(浅层合并) 的方式将我们传入的对象与旧的 state 进行合并。shallowMerge 只会合并新旧 state 对象中第一层的内容,如果 state 中对象的引用未变,那么 React 认为这个对象前后没有发生变化。所以如果我们以 mutable 的方式更改了 state 中的某个对象, React 会认为该对象并没有更新,那么相对应的 UI 就不会被重渲染。而以 Immutable 的方式更新 state 就不会出现这个问题

二十七、项目中如何使用不可变数据

一般来说,如果对象不是特别复杂可以直接使用结构赋值的方式,如果对象十分巨大那么可以使用immer这个开源库解决不可变数据的问题
immer的实现原理:原始对象先做了一层 Proxy 代理,得到 draftState 传递给 function。function(带副作用) 直接更改 draftState,最后 produce 返回新的对象

二十八、介绍一下React.memo

React.memo 为高阶组件。如果你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。React.memo 仅检查 props 变更。如果函数组件被 React.memo 包裹,且其实现中拥有 useState,useReducer 或 useContext 的 Hook,当 state 或 context 发生变化时,它仍会重新渲染。默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现,第二个参数是一个函数,返回true不渲染,false渲染

二十九、React.memo的使用场景

1、展示型组件

如果你有一个仅仅用于展示数据的组件,且数据不需要频繁更新,那么可以使用 React.memo 避免不必要的重新渲染。

2、性能瓶颈

如果某个组件是你应用中性能瓶颈的主要原因,那么可以使用 React.memo 优化它的性能。

3、模拟 PureComponent

如果你想在函数组件中模拟类组件的 PureComponent 行为,那么可以使用 React.memo

三十、React.PureComponent 和 React.memo的区别

1、继承关系

React.PureComponent 是一个 React 组件类,可以被继承;而 React.memo 是一个高阶组件,不能被继承。

2、比较方式

React.PureComponent 使用的是浅层比较(shallow comparison)来决定是否需要重新渲染组件;而 React.memo 是通过比较组件的 props 来决定是否需要重新渲染的。如果需要进行深层比较,则需要使用自定义的比较函数(comparison function)。

3、使用场景

React.PureComponent 适用于状态不多、不需要处理复杂逻辑的组件;而 React.memo 则适用于任何情况下,甚至可以代替 React.PureComponent

三十一、什么是useMemo

它可以帮助你避免在不必要的情况下重新渲染组件。它通过对比当前状态和前一个状态,决定是否重新计算或记忆一个值。

三十二、useMemo的使用场景

1、数据过滤

如果你需要在组件中过滤大量数据,并且数据不需要频繁更新,那么可以使用 useMemo 将过滤结果缓存,避免不必要的重新计算。

2、计算值

如果你需要在组件中计算某些值,并且这些值不需要频繁更新,那么可以使用 useMemo 将这些值缓存,避免不必要的重新计算。

3、预处理

如果你需要在组件中进行复杂的预处理,并且预处理结果不需要频繁更新,那么可以使用 useMemo 将预处理结果缓存,避免不必要的重新计算

三十三、什么是useCallback(这里可以和React.memo关联,和过期闭包关联)

在 React 中,当组件重新渲染时,它会重新执行所有的函数,因此在频繁更新的组件中使用多个函数会导致不必要的性能开销。useCallback 可以解决这个问题,它接受两个参数:一个回调函数和一个依赖项列表。当依赖项列表中的任意一项改变时,useCallback 会重新定义回调函数,否则它会返回一个缓存的函数引用,从而避免不必要的函数重新定义

三十四、useCallback 和 useMemo的区别

1、目的不同

useMemo 是用于缓存计算结果,useCallback 是用于缓存函数引用。

2、使用方法不同

useMemo 用于缓存计算结果,并在其依赖项发生变化时进行重新计算;而 useCallback 只是在依赖项发生变化时重新生成一个新的回调函数。

3、返回值不同

useMemo 返回缓存的计算结果,useCallback 返回一个缓存的回调函数

4、总结

总的来说,useMemo 适用于需要缓存计算结果的场景,useCallback 适用于缓存回调函数的场景。

三十五、hooks模仿componentDidMount

  useEffect(() => {
    /**
     * 当它是一个空数组时,回调只会被触发一次,类似于 componentDidMount
     */
    console.log("componentDidmount")
  }, [])

三十六、hooks模仿shouldComponentUpdate

import React, { useState, useEffect, useContext } from 'react';


const MyComponent =  React.memo((props) => {
  /* 使用 props 渲染 */
  return (
    <div>{props.num}</div>
  )
  /**
   * prevProps 上次的值
   * nextProps 最新的值
   * 
   * 如果传来的值是偶数的话则不更新组件
   */
}, (prevProps, nextProps) => {
  console.log(nextProps, nextProps.num % 2)
  return nextProps.num % 2 === 0
})

export default function hook() {

  const [num, setNum] = useState(1)

  useEffect(() => {
    /**
     * 当它是一个空数组时,回调只会被触发一次,类似于 componentDidMount
     */
    console.log("componentDidmount")
  }, [])

  return (
    <div>
      <button onClick={() => setNum(num + 1)}>+1</button>
      <MyComponent num={num}></MyComponent>
    </div>
  )
}

三十七、hooks模仿componentWillUnmount

  useEffect(() => {
    return () => {
      console.log('componentWillUnmount')
    }
  }, [])

三十八、什么是过期闭包

过期闭包(stale closure)是指一个闭包在创建之后,所引用的外部作用域内的变量已经被修改,但闭包内仍然保存了旧值。这就导致闭包中的代码与外部作用域内的实际状态不一致,从而造成错误的结果

三十九、react hook中的过时的闭包

在React中,过期闭包问题是指因为闭包的生命周期长于其引用的变量的生命周期而导致的问题。
在React组件的render函数中,如果使用了闭包引用组件的state或props,当state或props发生变化时,闭包将不会自动更新引用的变量。这就可能导致闭包引用了错误的值,从而导致组件的不正确行为。
四十六、react中遇到过那些过期闭包+做项目的时候遇到过那些坑吗

1、useEffect中过期闭包的表现

import React, { useState, useEffect, useContext } from 'react';

export default function hook() {

  const [count, setCount] = useState(0)
  /**
   * 每次点击都会调用,没切都是原来的值
   */
  useEffect(() => {
    // 是一个过时的闭包
    setInterval(() => {
      console.log(count)
    }, 2000)
  }, [])

  return (
    <div>
      {count}
      <button onClick={() => setCount(count + 1)}> 加1 </button>
    </div>
  )
}

2、useEffect解决方案

让useEffect()知道定时器的方法里面中的闭包依赖于count

import React, { useState, useEffect, useContext } from 'react';

export default function hook() {

  const [count, setCount] = useState(0)
  /**
   * 每次点击都会调用,没切都是原来的值
   */
  useEffect(() => {
    // 是一个过时的闭包
    const ter = setInterval(() => {
      console.log(count)
    }, 2000)
    
    // 每次调用前先清空定时器,或者说重新创建
    return () => {
      clearInterval(ter)
    }
    
    // 这行是重点,count变化后重新渲染useEffect
  }, [count])

  return (
    <div>
      {count}
      <button onClick={() => setCount(count + 1)}> 加1 </button>
    </div>
  )
}

3、useState过期闭包的表现

点击 +1 然后立即点击 +2,count 只更新到 1。这是因为 delay() 是一个过时的闭包

import React, { useState, useEffect, useContext } from 'react';

export default function hook() {

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

  /**
   *
   * delay() 是一个过时的闭包,它使用在初始渲染期间捕获的过时的 count 变量
   */
  function add() {
    setTimeout(function delay() {
      setCount(count + 1);
    }, 1000);
  }

  const add2 = () => {
    setCount(count + 2)
  }

  return (
    <div>
      {count}
      <button onClick={() => add()}>+1  </button>
      <button onClick={() => add2()}>+2</button>
    </div>
  )
}

4、useState解决方案

import React, { useState, useEffect, useContext } from 'react';

export default function hook() {

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

  /**
   *
   * delay() 是一个过时的闭包,它使用在初始渲染期间捕获的过时的 count 变量
   */
  function add() {
    setTimeout(function delay() {
      setCount((a) => a + 1);
    }, 1000);
  }

  const add2 = () => {
    setCount(count + 2)
  }

  return (
    <div>
      {count}
      <button onClick={() => add()}>+1  </button>
      <button onClick={() => add2()}>+2</button>
    </div>
  )
}

5、useCallback如果依赖项传一个空数组内部也会形成过期闭包

import React, { useState, useEffect, useContext } from 'react';

export default function hook() {

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

  /**
   *
   * delay() 是一个过时的闭包,它使用在初始渲染期间捕获的过时的 count 变量
   */
  function add() {
    setTimeout(function delay() {
      setCount((a) => a + 1);
    }, 1000);
  }

  const add2 = () => {
    setCount(count + 2)
  }

  useCallback(() => {
    // 值永远不会更新
    console.log(count)
  }, [])

  return (
    <div>
      {count}
      <button onClick={() => add()}>+1  </button>
      <button onClick={() => add2()}>+2</button>
    </div>
  )
}

标签:count,闭包,面试题,--,day02,React,useState,组件,useEffect
From: https://www.cnblogs.com/qzdgq/p/17493105.html

相关文章

  • Python中获取路径/文件的父目录
    本教程将讲解在Python中获取一个路径的父目录的各种方法。父目录是指高于或高于给定目录或文件的目录。例如,路径 C:\folder\subfolder\myfile.txt 的父目录是 C:\folder\subfolder。除了根目录外,每个目录都有一个父目录。1、使用 pathlib 模块的 path.parent() 方法获......
  • 锐捷交换机多链路 限速
    应用场景:Qospoliy-map可以关联访问控制列表ACL,实现基于特定报文流的限速(比如只针对网页的http的流量进行限速,或者是只针对XX网段的用户限速等),利用ACL可灵活配置的特点,可以满足客户对不同流量的定制化限速,当然这种方式的配置会相对复杂点,当客户有如上需求的时候可以考虑采用poli......
  • 保留两位小数
    保留两位小数问题:后台返的数据就是保留两位小数的数据,如4.00,5.25,展示在前端会是4,5.25,为了解决这个问题,通常会加parseInt('5.25').toFixed(2)方法,来保留两位小数,但是此方法有时候达不到预期。因为后台返的是字符串,不是数字toFixed()方法可把Number四舍五入为指定小数位数的......
  • 【已解决】XFTP 连接提示“SFTP 子系统申请已拒绝,请确保SSH连接的SFTP子系统设置有效
    一、报错信息报错:SFTP子系统申请已拒绝请确保SSH连接的SFTP子系统设置有效二、错误原因是ssh配置做了限制,修改一下配置文件就好了。三、解决方式修改 sshd_config 配置文件。vim/etc/ssh/sshd_config如果配置文件中不存在以下代码,找个位置添加上即可。......
  • 【HarmonyOS】如何解决智能穿戴设备中swiper组件右滑与系统退出应用冲突问题(API6 JS)
    【关键字】API6、JS、swiper组件、智能穿戴、setSwipeToDismiss 【问题描述】使用API6JS开发智能穿戴设备HarmonyOS应用,在首页使用swiper组件时,右滑swiper时会退出应用,无法实现swiper右滑效果,效果如下所示:​ 【问题分析与原因】当页面栈只有一个页面时,默认滑动事件分发......
  • 基于C语言的一维小波变换处理算法使用C语言实现的小波变换一维信号处理算法,以下是使用
    基于C语言的一维小波变换处理算法使用C语言实现的小波变换一维信号处理算法,以下是使用MATLAB和C语言算法的处理结果对比图。还可以提供说明文档对程序进行说明。涉及到的知识点和领域范围是信号处理和编程语言。小波变换是一种信号处理技术,用于分析和处理信号的频率和时间特性。C......
  • Ribbon-饥饿加载
    Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。 【LoadBalanceClient定义了从可用服务列表中选择一个具体的服务实例进行访问的逻辑】而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:ribbon:eager-load......
  • labview和西门子plc走以太网通信 这段话涉及到的知识点是LabVIEW和西
    labview和西门子plc走以太网通信这段话涉及到的知识点是LabVIEW和西门子PLC的以太网通信。LabVIEW是一种图形化编程环境,用于控制、测量和监视各种设备和系统。它可以与各种硬件设备进行通信,并提供了丰富的功能和工具来处理数据和执行自动化任务。西门子PLC(可编程逻辑控制器)是一种......
  • lazy-nvim插件管理器基础入门
    一篇通过使用lazy.nvim进行nvim插件管理的入门笔记。基础安装init.lua路径:stdpath("config")/init.luastdpath("config")macOS/Linux:~/.config/nvimWindows:~/AppData/Local/nvim--bootstraplazy.nvim--./lua/lazynvim-init.luarequire("lazynvim-init"......
  • Matlab仿真 三相可控桥式整流电路Matlab 仿真 三相可控桥式整流电路 Matlab Simulink
    Matlab仿真三相可控桥式整流电路Matlab仿真三相可控桥式整流电路MatlabSimulink仿真电力电子三相全控桥式整流电路Matlab文件,已经搭建好了的电路模型。电阻负载阻感负载都有,各个输出波形均正确。可直接使用。这是一个关于Matlab仿真和三相可控桥式整流电路的话题。在Matlab......