二十一、高阶组件的使用场景
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