什么类型网站,会展设计是什么,app手机网站建设黄,表白网页在线生成网站一、类组件和函数组件的区别#xff08;面试常考#xff09;
简单理解#xff08;所有同学都要掌握#xff09;
1、类组件有生命周期#xff0c;函数组件没有
2、类组件需要继承 Class#xff0c;函数组件不需要
3、类组件可以获取实例化的 this#xff0c;并且基于…一、类组件和函数组件的区别面试常考
简单理解所有同学都要掌握
1、类组件有生命周期函数组件没有
2、类组件需要继承 Class函数组件不需要
3、类组件可以获取实例化的 this并且基于 this 做各种操作函数组件不行
4、类组件内部可以定义并维护 state 函数组件都称为无状态了那肯定不行。
进阶理解挑战高薪
函数式组件捕获了渲染时所使用的值这是两类组件最大的不同
创建组件LearnComponentDiff
import React from react;import FCComponent from ./fc-component;
import ClassComponent from ./class-component;export default class LearnComponentDiff extends React.Component {state {user: 王凯,};render() {return (labelb选择一个人查看关注: /bselectvalue{this.state.user}onChange{(e) this.setState({ user: e.target.value })}option valuewangkai王凯/optionoption valuezhangsan张三/optionoption valuelisi李四/option/select/labelh1欢迎关注 {this.state.user}!/h1pFCComponent user{this.state.user} /b (函数组件)/b/ppClassComponent user{this.state.user} /b (类组件)/b/pp你能看出两者的不同吗/p/);}
}创建ClassComponent类组件
import React from react;class ClassComponent extends React.Component {showMessage () {alert(选择的 this.props.user);}handleClick () {setTimeout(this.showMessage, 3000);};render() {return button onClick{this.handleClick}尝试/button;}
}export default ClassComponent;点击页面后我们发现 初始化的props改变了 user 是通过 props 下发的props不可改变那么造成数据改变的原因就一定是 this 指向改变了。 真正的原因也确实如此虽然props不可改变但是this是可变的this.props 的每次调用都会去获取最新的 this 值这也是React保证数据实时性的重要手段。
那么就很清晰了当showMessage最终执行时此时的 this 绑定的是 张三对应的上下文所以输出为 ‘已关注 张三’
创建FCComponent函数组件
import React from react;function FCComponent(props) {const showMessage () {alert(已关注 props.user);}const handleClick () {setTimeout(showMessage, 3000);};return (button onClick{handleClick}尝试/button);
}export default FCComponent;最终的输出值明显为 ‘选择的 王凯’props 会在函数执行的瞬间就被捕获而 props 本身又是不可变值所以我们可以确保从当前开始读取到的 props 都是最初捕获到的。当父组件传入新的 props 尝试重新渲染函数时本质是基于新的 props 入参重新调用了一次函数并不会影响上一次调用。这就是 Dan 所说的函数式组件捕获了渲染所使用的值并且我们还能进一步意识到函数组件真正将数据和渲染紧紧的绑定到一起了
注意
很多人认为在函数组件中延迟输出的 state 是调用时的 state而不是最新的 state 是一个Bug恰恰相反这是一个函数式组件的特性是真正践行了React设计理念的正确方式。
当然Hooks也给出了获取最新的props和state的方法就是 useRef详细用法在我们的课程已经有所介绍同学们可以自行回顾一下
推荐阅读
大家可以看一下React 团队核心成员和 Redux 作者 Dan的这边文章。进阶理解这部分也借鉴了Dan大佬的这篇文章
函数式组件与类组件有何不同
二、除jsx外react还可以使用那些方式编写UI
当你不想在构建环境中配置有关 JSX 编译时不在 React 中使用 JSX 会更加方便。每个 JSX 元素只是调用 React.createElement(component, props, ...children) 的语法糖。因此使用 JSX 可以完成的任何事情都可以通过纯 JavaScript 完成
三、react中样式污染产生的原因
React最终编译打包后都在一个html页面中如果在两个组件中取一样类名分别引用在自身那么后者会覆盖前者。默认情况下只要导入了组件不管组件有没有显示在页面中组件的样式就会生效。也就是说并没有自己的局部作用域
四、如何解决react中的样式污染
1、手动处理 起不同的类名但是项目一大就会导致类名很乱不利于团队协作
2、CSS IN JS 以js的方式来处理css推荐
五、什么是CSS IN JS
CSS IN JS是使用 JavaScript 编写 CSS 的统称用来解决 CSS 样式冲突、覆盖等问题。
CSS IN JS 的具体实现有 50 多种比如React常用CSS Modules、styled-components、 Vue常用style scoped 、css modules等
六、CSS module的实现原理
CSS Modules 通过对 CSS 类名重命名保证每个类名的唯一性从而避免样式冲突的问题。也就是说所有类名都具有“局部作用域”只在当前组件内部生效。在 React 脚手架中文件名、类名、hash随机三部分只需要指定类名即可 BEM
七、什么是合成事件
React 合成事件SyntheticEvent是 React 模拟原生 DOM 事件所有能力的一个事件对象即浏览器原生事件的跨浏览器包装器。它根据 W3C 规范 来定义合成事件兼容所有浏览器拥有与浏览器原生事件相同的接口。
八、合成事件的优势
1、进行浏览器兼容实现更好的跨平台
React 采用的是顶层事件代理机制能够保证冒泡一致性可以跨浏览器执行。React 提供的合成事件用来抹平不同浏览器事件对象之间的差异将不同平台事件模拟合成事件。
2、避免垃圾回收
事件对象可能会被频繁创建和回收因此 React 引入事件池在事件池中获取或释放事件对象。即 React 事件对象不会被释放掉而是存放进一个数组中当事件触发就从这个数组中弹出避免频繁地去创建和销毁(垃圾回收)。
进阶理解挑战高薪
3、方便事件统一管理和事务机制
有兴趣的同学可以看一下源码的实现https://github.com/facebook/react/blob/75ab53b9e1de662121e68dabb010655943d28d11/packages/events/SyntheticEvent.js#L62
九、在类组件定义事件时this指向的问题
一般来说我们都是直接使用箭头函数然后后面跟上一个事件也可以使用呢call bind apply直接改变事件
这里要注意箭头函数的定义事件的性能问题call bind apply的坑
十、state同步还是异步的
分版本来讲在 react17 中setState 是批量执行的因为执行前会设置 executionContext。但如果在 setTimeout、事件监听器等函数里就不会设置 executionContext 了这时候 setState 会同步执行。可以在外面包一层 batchUpdates 函数手动设置下 excutionContext 来切换成异步批量执行。
在react 18里都是异步的
进阶理解挑战高薪
此处为语雀内容卡片点击链接查看第13节——React Fiber 架构 · 语雀
十一、为什么map的时候要加key
key是react用来追踪哪些列表的元素被修改被添加或者是被删除的辅助标示。在开发过程中我们需要保证某个元素的key在其同级元素中具有唯一性。
在react的diff算法中react会借助元素的key来判断该元素是最新创建的还是被移动而来的从而减少不必要的元素渲染。除此之外react还要根据key来判断元素与本地状态的关联关系。
十二、为什么index不能当做key
当我们用index做下标的时候点击删除列表中的每一项的下标都会发生变化如果用下标当做key就会触发dom重新渲染
十三、react组件通讯
父传子
在父组件里的子组件标签上定义属性在子组件里使用props接收
子传父
父组件给子组件传入一个方法子组件接收这个方法在对应的事件里触发接收到的方法并且可以传参。子传父本质上来说就是通过观察者去触发了一个回调函数。
十三、什么是受控组件
在HTML中表单元素的标签input、textarea、select等这些值改变通常是根据用户输入进行更新。那么在React中可变状态通常保存在组件的状态属性中并且只能使用 setState() 进行更新 如果这个组件里面存放着一些表单控件我们也可以手动的进行组件更新那么以这种由React控制的输入表单元素而改变其值的方式称为受控组件
十四、状态提升
在React框架中当多个组件需要反映相同的变化数据这时建议将共享状态提升到最近的共同父组件中去。一般我们将该操作称为状态提升。由此我们可以很清楚的明白React状态提升主要就是用来处理父组件和子组件的数据传递的他可以让我们的数据流动的形式是自顶向下单向流动的所有组件的数据都是来自于他们的父辈组件也都是由父辈组件来统一存储和修改再传入子组件当中。
十五、在项目中封装过哪些组件如何封装一个弹框组件
使用画一个弹框的UI使用固定定位定到屏幕中央然后确定接收的参数如标题弹框内容按钮名等暴露出两个按钮的点击事件。最后使用ReactDOM.createPortal创建这个组件
十六、ReactDOM.createPorta
Portal 提供了一种将子节点渲染到存在于父组件以外 DOM 节点的方案。在 CSS 中我们可以使用 position: fixed 等定位方式让元素从视觉上脱离父元素。在 React 中Portal 直接改变了组件的挂载方式不再是挂载到上层父节点上而是可以让用户指定一个挂载节点。一般用于模态对话框工具提示、悬浮卡、加载动画等
十七、生命周期
全文背诵并且回答出执行时间和使用场景。注意这里不要回答报错后触发的那两个生命周期
十八、为什么react17后删除了那些生命周期
react 打算在17版本推出新的 Async Rendering提出一种可被打断的生命周期而可以被打断的阶段正是实际 dom 挂载之前的虚拟 dom 构建阶段也就是要被去掉的三个生命周期。本身这三个生命周期所表达的含义是没有问题的但 react 官方认为我们开发者也许在这三个函数中编写了有副作用的代码所以要替换掉这三个生命周期因为这三个生命周期可能在一次 render 中被反复调用多次
十九、错误边界
是 React 组件并不是损坏的组件树。错误边界捕捉发生在子组件树中任意地方的 JavaScript 错误打印错误日志并且显示回退的用户界面。错误边界捕捉渲染期间、在生命周期方法中和在它们之下整棵树的构造函数中的错误。
二十、什么是fiber
Fiber 是一个基于优先级策略和帧间回调的循环任务调度算法的架构方案。随着应用变得越来越庞大整个更新渲染的过程开始变得吃力大量的组件渲染会导致主进程长时间被占用导致一些动画或高频操作出现卡顿和掉帧的情况。而关键点便是 同步阻塞。在之前的调度算法中React 需要实例化每个类组件生成一棵组件树使用 同步递归 的方式进行遍历渲染而这个过程最大的问题就是无法 暂停和恢复。
进阶理解挑战高薪
课件全部背过
此处为语雀内容卡片点击链接查看第13节——React Fiber 架构 · 语雀
二十一、什么context
上下文 (Context) 是 React 中一个重要的特性它允许您在组件树中共享数据而不必通过每个级别显式地传递参数。这是一种将数据传递到树中任意深度的组件的方法无论其祖先组件是否知道该数据。
二十二、context的使用场景
1、主题色切换。
2、多国语言切换。也就是国际化
3、祖孙组件之间的传值
二十三、context祖孙传值
1、父组件
import React from react;
import LearnContext2 from ./LearnContext2;const MyContext React.createContext();export default class LearnContext extends React.Component {state {num: 1,};/*** 接收所有层级的子组件返回的消息* param {} msg*/getChildData (msg) {console.log(msg);};render() {return (divdiv父级组件/divMyContext.Provider/*** 给所有的子级组件传一个num的值* 并且传一个方法 让他们也可以和父级组件通讯*/value{{ num: this.state.num, getChildData: this.getChildData }}LearnContext2/LearnContext2/MyContext.Provider/div);}
}2、子组件
import React from react;
import LearnContext3 from ./learn-context3;export default class LearnContext2 extends React.Component {render() {return (divdiv子组件/divLearnContext3/LearnContext3/div);}
}3、孙组件
使用 Consumer 接收传下来的context
import React from react;
import { MyContext } from ./learn-context;class LearnContext3 extends React.Component {render() {return (MyContext.Consumer{/* 直接使用Consumer这种方式获取参数但是这种方式 不方便在render意外的地方使用传下来的参数*/}{(context) (divdiv孙组件/divdiv爷爷——传下来的{context.num}/divbutton onClick{() context.onChildHandle(哈喽爷爷)}给爷爷问声好/button/div)}/MyContext.Consumer);}
}export default LearnContext3;使用contextType 接收context
import React from react;
import { MyContext } from ./learn-context;class LearnContext4 extends React.Component {// 可以直接在类里面使用static 声明静态属性static contextType MyContextrender () {return (divdiv孙组件/div{/* 当我们把context挂载到当前组件的contextType的时候就可以直接使用this.context拿到相关的值*/}div爷爷——传下来的{this.context.num}/divbutton onClick{() this.context.onChildHandle(哈喽爷爷)}给爷爷问声好/button/div)}
}// 也可以直接在这上面进行赋值
LearnContext4.contextType MyContextexport default LearnContext4
二十四、为什么会出现ref
在react典型的数据流中props传递是父子组件交互的唯一方式通过传递一个新的props值来使子组件重新re-render,从而达到父子组件通信。当然就像react官网所描述的一样在react典型的数据流之外某些情况下例如和第三方的dom库整合或者某个dom元素focus等为了修改子组件我们可能需要另一种方式这就是ref方式。
二十五、当ref值是一个函数什么时候会被调用以及参数会有什么表现
React 会在组件挂载时调用 ref 回调函数并传入 DOM元素(或React实例)当卸载时调用它并传入 null。在 componentDidMount 或 componentDidUpdate 触发前React 会保证 Refs 一定是最新的。
二十六、什么是高阶组件
高阶组件Higher-Order ComponentHOC是 React 中一种代码重用的模式它是一个函数接受一个组件作为参数并返回一个新的组件。它的作用是对组件进行抽象从而在不修改组件代码的情况下扩展组件的功能
二十六、高阶组件的使用场景
1、数据获取高阶组件可以在组件挂载时自动获取数据并将数据通过 props 传递给被包装组件。
2、权限控制高阶组件可以检查用户是否有访问该组件的权限从而决定是否渲染该组件。
3、代码重用高阶组件可以通过封装一些常见的逻辑来提高代码的复用性。
4、状态管理高阶组件可以管理一些状态并通过 props 将状态传递给被包装组件如redux里的connect。
5、表单处理高阶组件可以处理表单的数据包括数据验证、数据提交等antd里面的Form。
6、设计模式高阶组件可以作为设计模式的一部分例如实现观察者模式、策略模式等
二十七、什么是render props
Render props 是一种在 React 中实现代码复用的方法。它允许你在一个组件中定义逻辑并在另一个组件中渲染这个逻辑。这个技术通过一个特殊的 props称为 “render props”来实现复用。
二十八、render props的使用场景
1、实现代码复用如果多个组件需要实现相似的逻辑可以使用 render props 实现代码复用。
2、动态渲染内容通过 render props可以在不同的场景下动态渲染内容达到更好的可定制性。
3、访问组件的状态使用 render props 可以访问组件的状态从而在不同的场景下呈现不同的内容
二十九、为什么会出现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 (divbutton onClick{() setNum(num 1)}1/buttondiv你好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 包裹且其实现中拥有 useStateuseReducer 或 useContext 的 Hook当 state 或 context 发生变化时它仍会重新渲染。默认情况下其只会对复杂对象做浅层对比如果你想要控制对比过程那么请将自定义的比较函数通过第二个参数传入来实现,第二个参数是一个函数返回true不渲染false渲染
三十五、React.memo的使用场景
1、展示型组件
如果你有一个仅仅用于展示数据的组件且数据不需要频繁更新那么可以使用 React.memo 避免不必要的重新渲染。
2、性能瓶颈
如果某个组件是你应用中性能瓶颈的主要原因那么可以使用 React.memo 优化它的性能。
3、模拟 PureComponent
如果你想在函数组件中模拟类组件的 PureComponent 行为那么可以使用 React.memo
三十六、React.PureComponent 和 React.memo的区别
、继承关系
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 (divbutton onClick{() setNum(num 1)}1/buttonMyComponent num{num}/MyComponent/div)
}
四十三、hooks模仿componentWillUnmount useEffect(() {return () {console.log(componentWillUnmount)}}, [])
四十四、封装hooks 实现componentDidUpdate的部分功能
// 引入我们封装的自定义hooks
import useDidUpdate from ./hooks/use-didupdateexport default () {useDidupdate(() {// 每次初始化的时候不触发每次render都会触发console.log(模仿componentDidUpdate)})
}四十四、什么是过期闭包
过期闭包stale closure是指一个闭包在创建之后所引用的外部作用域内的变量已经被修改但闭包内仍然保存了旧值。这就导致闭包中的代码与外部作用域内的实际状态不一致从而造成错误的结果
四十五、react hook中的过时的闭包
在React中过期闭包问题是指因为闭包的生命周期长于其引用的变量的生命周期而导致的问题。
在React组件的render函数中如果使用了闭包引用组件的state或props当state或props发生变化时闭包将不会自动更新引用的变量。这就可能导致闭包引用了错误的值从而导致组件的不正确行为。
四十六、react中遇到过那些过期闭包做项目的时候遇到过那些坑吗
2、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)
}
3、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)
}
4、useState过期闭包的表现
点击 1 然后立即点击 2count 只更新到 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 /buttonbutton onClick{() add2()}2/button/div)
}
5、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 /buttonbutton onClick{() add2()}2/button/div)
}
6、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 /buttonbutton onClick{() add2()}2/button/div)
}
四十六、什么场景下需要使用useReducer
1、当多个 state 需要一起更新时
2、当state 更新逻辑较复杂
3、当下一个 state 依赖于之前的 state即 编写 setState(prevState newState)时
包括但不限于以上三种
四十七、useReducer()相对于 useState() 的优势
1、useReducer 相对于 useState 可以更好的描述“如何更新状态”。 比如useReducer 能够读取相关的状态、同时更新多个状态。
2、组件负责发出 actionreducer 负责更新状态的模式 使得代码逻辑更加清晰。代码行为更加可以预测比useEffect 的更新时机更加稳定
3、通过传递 dispatch ,可以减少状态值的传递。 useReducer 总是返回相同的 dispatch 函数这是彻底解耦的标志状态更新逻辑可以任意变化而发起 action 的渠道始终不变。
四十八、useRef事来解决什么问题的
1、JSX组件转换后对应的真实DOM对象。如何获取input的真实dom
useRef只适合“勾住”小写开头的类似原生标签的组件。如果是自定义的react组件(自定义的组件必须大写字母开头)那么是无法使用useRef当然也有一些奇淫技巧后面我们后续课程会讲的。比如我们如何获取这个 input/ 真实DOM呢。那么就可以使用useRef
初始化input聚焦
import React,{useEffect,useRef} from reactfunction Component() {//先定义一个inputRef引用变量用于“勾住”挂载网页后的输入框const inputRef useRef(null);useEffect(() {//inputRef.current就是挂载到网页后的那个输入框一个真实DOM因此可以调用html中的方法focus()inputRef.current.focus();},[]);return div{/* 通过 ref 属性将 inputRef与该输入框进行“挂钩” */}input typetext ref{inputRef} //div
}
export default Component
2、在useEffect中创建的变量如何在useEffect 以外的地方获取并引用
例子
1、组件中有一个变量count当该组件挂载到网页后count每秒自动 1。
2、组件中有一个按钮点击按钮可以停止count自动1
import React,{useState,useEffect,useRef} from reactfunction Component() {const [count,setCount] useState(0);const timerRef useRef(null);//先定义一个timerRef引用变量用于“勾住”useEffect中通过setIntervale创建的计时器useEffect(() {//将timerRef.current与setIntervale创建的计时器进行“挂钩”timerRef.current setInterval(() {setCount((prevData) { return prevData 1});}, 1000);return () {//通过timerRef.current清除掉计时器clearInterval(timerRef.current);}},[]);const clickHandler () {//通过timerRef.current清除掉计时器clearInterval(timerRef.current);};return (div{count}button onClick{clickHandler} stop/button/div)
}export default Component
4、父组件调用子组件内的方法
注意:除非情况非常特殊否则一般情况下都不要采用 父组件调用子组件的函数 这种策略
实现思路
父组件中通过 useRef 定义一个钩子变量例如 childFunRef
父组件通过参数配置将 childFunRef 传递给子组件
子组件在自己的 useEffect() 中定义一个函数例如 doSomting()
划重点一定要在 useEffect() 中定义 doSomting()不能直接在子组件内部定义。
因为如果 doSomting() 定义在子组件内部那么就会造成每一次组件刷新都会重新生成一份 doSomthing()
然后将 doSomting() 赋值到 childFunRef.current 中
这样当父组件想调用子组件中的 doSomting() 时可执行 childFunRef.current.doSomting()
父组件
import { useRef } from react;
import ChildComponent from ./child;const ParentComponent () {const childFunRef useRef();const handleOnClick () {if (childFunRef.current) {childFunRef.current.doSomething();}};return (divChildComponent funRef{childFunRef} /button onClick{handleOnClick}执行子项的doSomething()/button/div);
};export default ParentComponent;
子组件
import { useEffect, useState } from react;const ChildComponent ({ funRef }) {const [num, setNum] useState(0);useEffect(() {const doSomething () {setNum(Math.floor(Math.random() * 100));};funRef.current { doSomething }; //在子组件中修改父组件中定义的childFunRef的值}, [funRef]);return div{num}/div;
};export default ChildComponent;
四十九、useRef与createRef的区别
1、useRef是针对函数组件的如果是类组件则使用React.createRef()。
2、React.createRef()也可以在函数组件中使用。useRef只能在react hooks中使用
3、createRef每次都会返回个新的引用;而useRef不会随着组件的更新而重新创建
五十、介绍一下useImperativeHandle
useImperativeHandle可以让父组件获取并执行子组件内某些自定义函数(方法)。本质上其实是子组件将自己内部的函数(方法)通过useImperativeHandle添加到父组件中useRef定义的对象中。
注意 1、useRef创建引用变量 2、React.forwardRef将引用变量传递给子组件 3、useImperativeHandle将子组件内定义的函数作为属性添加到父组件中的ref对象上。
因此如果想使用useImperativeHandle那么还要结合useRef、React.forwardRef一起使用。
五十一、什么是React.forwardRef
React.forwardRef 会创建一个React组件这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。
五十二、useLayoutEffect和useEffect的区别
你可以把useLayoutEffect等同于componentDidMount、componentDidUpdate因为他们调用阶段是相同的。而useEffect是在componentDidMount、componentDidUpdate调用之后才会触发的。
也就是说当组件所有DOM都渲染完成后同步调用useLayoutEffect然后再调用useEffect。
useLayoutEffect永远要比useEffect先触发完成
五十三、你知道那些调试项目的手段
useDebugValueReact Developer Tools
详细看第36节——useDebugValueReact Developer Tools
五十四、什么是Concurrent React
并发可以理解为同时执行大量任务的能力。并发并不是一个特性。相反它是一个幕后功能它允许 React 同时并发地准备许多 UI 实例。
并发涉及同时执行多个状态更新这可以说是 React 18 中最重要的特性。除了并发之外React 18 还引入了两个新的钩子称为 useTransition() 和 useDeferredValue() 钩子。
它们都有助于降低状态更新的优先级
五十五、useDeferredValue和useTransition的作用
useDeferredValue 和useTransition这两个钩子可以让我们延迟渲染不紧急的部分类似于防抖但没有固定的延迟时间延迟的渲染会在紧急的部分先出现在浏览器屏幕以后才开始并且可中断不会阻塞用户输入。
简单理解就是如果说某些渲染比较消耗性能比如存在实时计算和反馈我们可以使用这个Hook降低其计算的优先级使得避免整个应用变得卡顿。较多的场景可能就在于用户反馈输入上。比如百度的输入框用户在输入的时候页面会发生变化但是输入框输入并不卡顿
五十六、useDeferredValue vs useTransition
1、相同点
useDeferredValue 本质上和内部实现与 useTransition 一样都是把任务标记成了过渡更新任务。
2、不同点
useTransition 是把 startTransition 内部的更新任务变成了过渡任务transtion,而 useDeferredValue 是把原值通过过渡任务得到新的值这个值作为延时状态。 也就是说一个是处理一段逻辑另一个是生产一个新的状态。
useDeferredValue 还有一个不同点就是这个任务本质上在 useEffect 内部执行而 useEffect 内部逻辑是异步执行的 所以它一定程度上更滞后于 useTransition。可以理解成useDeferredValue useEffect transtion
五十七、useTransition或者useDeferredValue和防抖的区别
1、节流防抖本质是 setTimeout 只不过控制了执行的频率原理就是让 render 次数减少了。而 transitions 和它相比并没有减少渲染的次数。
2、节流和防抖需要有效掌握延迟时间如果时间过长那么给人一种渲染滞后的感觉如果时间过短那么就类似于 setTimeout(fn,0) 还会造成前面的问题。而 startTransition 就不需要考虑这么多
五十八、useId解决什么问题
1、React在服务端渲染生成随机id假设为0.1这一步叫dehydrate脱水
2、div id0.1哈哈哈/div作为HTML传递给客户端作为首屏内容
3、React在客户端渲染生成随机id假设为0.2这一步叫hydrate注水
就会出现客户端、服务端生成的id不匹配
当然服务端、客户端无法简单生成稳定、唯一的id是个由来已久的问题早在15年就有人提过issue
https://github.com/facebook/react/issues/4000
那么react的useId就是用来解决这个问题的
五十九、useId的实现原理一般了解
身份生成算法
身份 id 是 32 进制的字符串其二进制表示对应树中节点的位置。
每次树分叉成多个子节点时我们都会在序列的左侧添加额外的位数表示子节点在当前子节点层级中的位置。 00101 00010001011010101╰─┬─╯ ╰───────┬───────╯Fork 5 of 20 Parent id
这里我们使用了两个前置 0 位。如果只考虑当前的子节点我们使用 101 就可以了但是要表达当前层级所有的子节点三位就不够用。因此需要 5 位。
出于同样的原因slots 是 1-indexed 而不是 0-indexed 。否则就无法区分该层级第 0 个子节点与父节点。
如果一个节点只有一个子节点并且没有具体化的 id声明时没有包含 useId hook。那么我们不需要在序列中分配任何空间。例如这两颗数会产生相同的 id Indirection A /A / B //Indirection /B /
/
为了处理这种情况每次我们生成一个 id 时都会分配一个一个新的层级。当然这个层级就只有一个节点「长度为 1 的数组」。
最后序列的大小可能会超出 32 位发生这种情况时我们通过将 id 的右侧部分转换为字符串并将其存储在溢出变量中。之所以使用 32 位字符串是因为 32 是 toString() 支持的 2 的最大幂数。这样基数足够大就能够得到紧凑的 id 序列并且我们希望基数是 2 的幂因为每个 log2(base) 对应一个字符也就是 log2(32) 5 bits 1 这样意味着我们可以在不影响最终结果的情况下删除末尾 5 的位。
六十、useInsertionEffect出现的原因
这个钩子与其他钩子略有不同因为它的唯一目的是对CSS-in-JS库很重要这些库在运行中生成新的规则并在文档中插入style 标签。
在某些情况下style 标签需要在客户端生成或编辑如果不小心的话在并发渲染中会造成性能问题。这是因为当CSS规则被添加或删除时浏览器必须检查这些规则是否适用于现有的树。它必须重新计算所有的样式规则并重新应用它们--而不仅仅是改变了的那些。如果React发现另一个组件也产生了一个新的规则同样的过程会再次发生。
这实际上意味着CSS规则必须在React渲染时针对每一帧的所有DOM节点进行重新计算。虽然你很有可能不会遇到这个问题但它的扩展性并不好。
为了解决这个问题React团队引入了useInsertionEffect Hook。它与useLayoutEffect Hook非常相似但它不能访问DOM节点的引用。
六十一、react-router中有哪些路由模型
1、HashRouter
HashRouter使用URL的哈希部分即#后面的部分来匹配路由它不会向服务器发送请求。例如URL可以是Example Domain。HashRouter兼容性比较好哪怕浏览器不支持HTML5 History API也可以正常使用。
2、BrowserRouter
BrowserRouter使用HTML5 History API来匹配路由使用 HTML5 的 pushState 和 replaceState API 来实现路由的切换。它可以隐藏URL中的#符号使URL更加友好。例如URL可以是http://example.com/about
3、MemoryRouter
MemoryRouter是一个不依赖于浏览器历史记录的路由器。它将URL存储在内存中而不是浏览器历史记录中适用于测试或在不支持HTML5 History API的环境中使用
4、StaticRouter
StaticRouter是一个用于服务器端渲染的路由器。它将请求的URL作为参数传递给组件并将组件的输出发送回客户端。这样就可以在服务器端生成动态HTML然后将其发送到浏览器。
5、NativeRouter
NativeRouter是用于React Native应用的路由器它使用Native导航而不是HTML导航来匹配路由
六十二、react中有哪些路由跳转方式
1、普通跳转
import { useNavigate } from react-router-dom;export default function APage() {/*** 使用useNavigate钩子返回一个方法* 使用这个方法进行跳转*/const navigate useNavigate();const linlB () {// 直接跟我们定义的pathnavigate(/b)}return (divdivA页面/divbutton onClick{() linlB()}跳转到B页面/button/div);
}2、替换当前页面
import { useNavigate } from react-router-dom;export default function APage() {/*** 使用useNavigate钩子返回一个方法* 使用这个方法进行跳转*/const navigate useNavigate();const replaceB () {// 直接跟我们定义的pathnavigate(/b, { replace: true })}return (divdivA页面/divbutton onClick{() replaceB()}把当前页面替换成B页面/button/div);
}3、前进或后退到浏览器历史记录中的特定页面
function MyComponent() {const navigate useNavigate();function handleBack() {// 后退几页navigate(-1);}function handleForward() {// 前进几页navigate(1);}return (divbutton onClick{handleBack}Back/buttonbutton onClick{handleForward}Forward/button/div);
}
六十三、react路由传参
1. 路由参数params形式
路由参数是将参数嵌入到 URL 中的一种方式。在 React Router 6 中我们可以通过在路由路径中使用冒号来定义参数
定义
Route path/users/:id element{UserDetails /} /
获取
我们定义了一个名为 id 的参数它可以在 URL 中的 /:id 部分找到。当用户访问 /users/123 时123 将成为路由参数并可以在组件中通过 useParams 钩子函数访问
function UserDetails() {const { id } useParams();// ...
} 2、查询参数search形式
查询参数是在 URL 中使用问号传递的一种参数。在 React Router 6 中我们可以通过在 URL 中添加查询参数来传递参数
定义
Link to/users?id123User Details/Link
获取
我们向 /users 页面传递了一个名为 id 的查询参数并将其设置为 123。我们可以在组件中使用 useLocation 钩子函数获取当前 URL 中的查询参数并使用 URLSearchParams 对象来解析它们
function UserDetails() {const searchParams useSearchParams();// 使用URLSearchParams这个对象解析url的search然后直接获取idconst id searchParams.get(id);// ...
}
3. 状态对象
状态对象是一种可以在导航期间传递数据的机制。在 React Router 6 中我们可以在 navigate 函数中使用第二个参数来传递状态对象
定义
function handleClick() {navigate(/users, { state: { id: 123 } });
}
获取
我们在导航到 /users 页面时传递了一个名为 id 的状态对象。我们可以在组件中使用 useLocation 钩子函数获取当前 URL 中的状态对象
function UserDetails() {const location useLocation();const { id } location.state;// ...
}
注意
使用状态对象传递数据会将数据存储在浏览器的会话历史中因此它仅适用于页面之间的相邻导航。如果用户从当前页面返回到其他页面状态对象中的数据将被清除。如果需要在不同页面之间共享数据最好使用其他的数据传递方式如 Redux 或 Context API
六十四、react如何实现嵌套路由
使用Outlet实现
在路由组件里直接定义
六十五、代码分割路由懒加载
React Router 在使用时会把所有路由相关的组件都打包进同一个 JavaScript 文件中这会导致整个应用的体积变得很大加载时间变长。为了解决这个问题我们可以使用代码分割code splitting技术将不同的路由组件分别打包成不同的 JavaScript 文件实现按需加载。
六十六、路由 (V5) 和路由 (V6) 的区别
react Router v6使用Hooks来实现路由而v5使用高阶组件HOCs来实现路由。这是它们之间最大的区别。
1、路由配置
React Router v5中的路由配置需要将Route组件作为子组件嵌套在Switch组件中。而React Router v6中的路由配置方式发生了变化。现在我们需要在Routes组件中使用数组来配置路由。
2、嵌套路由
在React Router v6中嵌套路由的使用方式更加简单直观。在v5中嵌套路由需要在组件之间进行深度传递props而在v6中可以使用嵌套路由。
3、状态管理
React Router v6通过提供useSearchParams、useLocation和useNavigate等Hooks使得状态管理变得更加方便。这些Hooks可以帮助我们在不同的路由之间共享状态而在v5中需要使用类似于redux等外部状态管理库来实现。
六十七、如何在类组件中使用最新版的路由v6
编写一个高阶组件
import React from react;
import { useNavigate } from react-router-dom;export default function WithRouter(WarpComponent) {const navigate useNavigate();return WarpComponent {...this.props} navigate{navigate}/WarpComponent;
}六十八、什么是redux
Redux 是一种 JavaScript 库用于管理应用的全局状态。它的目的是帮助开发者管理和同步应用中的数据状态以实现组件间的数据共享和通信。
Redux 遵循了一种单向数据流的架构模式将整个应用的状态数据存储在一个全局的状态树即 store中并通过明确的操作比如 dispatch 一个 action来修改数据状态。这样可以有效地降低数据状态的耦合度使得代码更加可维护和可读。
Redux 还支持中间件middleware和插件plugins允许开发者扩展其功能以适应不同的业务需求。它也支持热加载hot reloading可以在不重启应用的情况下更新代码。
总的来说Redux 是一个用于简化应用状态管理的工具广泛应用在 React 和其他前端框架中。
六十九、什么是redux store
Redux 是一个用于管理 JavaScript 应用状态的库。在 Redux 中整个应用的状态都存储在一个对象中称为 store。
Store 实际上是一个 JavaScript 对象它存储了整个应用的状态。它是唯一的意味着应用中只有一个 store。每当状态发生变化它会存储最新的状态。
使用 Redux 时你可以通过调用 store.getState() 来获取当前应用的状态通过调用 store.dispatch(action) 来更新应用的状态其中 action 是一个描述发生了什么的对象。
七十、如何分割state或者说分割reducer
使用combineReducers可以对redux进行模块化管理在 Redux 中你可以使用多个 Reducer 来处理不同的数据然后使用 combineReducers 函数将它们合并起来。
七十一、什么是action
在 Redux 中Action 是一个简单的 JavaScript 对象用于描述对应应用中的某个事件例如用户操作所发生的变化。它包含了一个 type 属性用于表示事件的类型以及其他一些可选的数据。
Action 可以被 Redux Store 中的 reducer 函数捕获并处理从而对应用的状态进行更新。通过使用 Action可以实现可预测、可追踪和可测试的应用状态管理。
七十二、什么是reducer
Reducer 是 Redux 中的一个概念它是一个纯函数用于处理应用的状态变更。Reducer 的输入是当前状态和一个操作action输出是下一个状态。
在 Redux 中所有的状态变更都必须通过发送一个 action 实现。每一个 action 都是一个描述状态变更的对象包含了一个 type 属性和一些其他属性。当一个 action 被发送到 store它会触发 store 对应的 reducer使用当前状态和 action 来生成下一个状态。
七十三、为什么redux中要使用不可变数据
Redux 要求使用不可变数据是因为它遵循了函数式编程的原则。在函数式编程中数据不可变是一项重要的原则这有助于避免状态更改产生的不可预知的副作用。
在 Redux 中每当 action 被分发reducer 都会接收到当前的状态和 action并返回一个新的状态。如果使用的是可变数据类型并且在 reducer 中直接对状态进行修改就会造成状态的不可预知的更改。
因此Redux 要求使用不可变数据是为了更好地管理应用状态以及提高代码的可读性和可维护性
七十四、redux中如何使用中间件
applyMiddleware 是 Redux 的一个高阶函数用于向 Redux Store 应用中间件。
中间件是一个函数它可以在 dispatch 操作执行前后对 action 进行拦截、处理、修改等操作。例如日志记录、错误捕获、异步请求、数据缓存等等。
使用 applyMiddleware你可以实现额外的功能并且可以在不修改原始代码的情况下对其进行扩展。
七十五、项目中有封装过哪些中间件
import { produce } from immer;// 定义immerMiddleware中间件/*** * store createStore后返回的store可以使用他的任何方法* next * returns */
const immerMiddleware store next action {// 使用immer的produce函数生成新的stateconst newState produce(store.getState(), draft {/*** next可以调用对应的action里面的reducer* 并且可以拿到reducer返回的结果* 我们把返回的结果给draft赋值*/draft next(action);});// 返回新的statereturn newState;
};export default immerMiddleware;七十六、如何封装封装actions模块
bindActionCreators 是 Redux 中的一个函数用于将多个 action creators 绑定到 dispatch 函数上使得可以在 Redux 应用中轻松调用这些 action creators。
七十八、redux中如何编写异步代码
使用redux-thunkredux-thunk 是一个用于处理 Redux 异步 action 的中间件。它允许我们在 Redux 中编写异步的 action 创建函数action creator这些 action 创建函数可以返回一个函数而不是一个 action 对象。这个返回的函数可以接收 dispatch 和 getState 两个参数并在函数体内进行异步操作后再使用 dispatch 方法触发一个或多个普通的同步 action
七十九、redux-thunk的工作原理
源码解读
function thunkMiddleware({ dispatch, getState }) {return next action {// 如果 action 是一个函数那么调用它并传递 dispatch 和 getState 函数作为参数if (typeof action function) {return action(dispatch, getState);}// 否则调用下一个中间件return next(action);};
}八十、flux架构
Flux 是一种由 Facebook 提出的前端应用程序架构旨在解决传统的 MVCModel-View-Controller架构在复杂应用场景下出现的一系列问题。Flux 基于单向数据流的思想将应用程序拆分为四个主要组件Action、Dispatcher、Store 和 View。
此处为语雀内容卡片点击链接查看第51节——Flux 架构 · 语雀
九十、介绍一下useSyncExternalStoreredux 的实现原理
useSyncExternalStore 是一个新的钩子它通过强制的同步状态更新使得外部 store 可以支持并发读取。它实现了对外部数据源订阅时不在需要 useEffect并且推荐用于任何与 React 外部状态集成的
九十一、介绍一下Redux ToolkitRTK
简化Redux的配置
Redux Toolkit提供了一个createSlice函数可以用来快速创建Redux的action和reducer不需要手动编写大量的模板代码。
封装常用的Redux函数
Redux Toolkit提供了一些封装过的Redux函数如createAsyncThunk、createEntityAdapter等这些函数可以帮助开发者更加容易地处理异步操作、管理实体数据等常见任务。
整合常用的中间件
Redux Toolkit默认集成了常用的中间件如redux-thunk、redux-logger等使得开发者可以更加便捷地使用这些中间件而不需要手动配置。
提供默认的Redux store配置
Redux Toolkit提供了一个configureStore函数可以用来快速创建一个Redux store并且默认配置了许多常用的中间件和插件减少了开发者的配置工作量。
九十二、介绍一下redux-toolkit中的configureStore
configureStore是Redux Toolkit中的一个工厂函数用于创建Redux store。它的目的是简化Redux store的设置并提供许多默认设置和内置的中间件使其更易于使用。
九十三、什么是切片
在 Redux 中一个 state tree 是由多个 reducer 组成的每个 reducer 负责管理一个 state 的一部分这个概念称为“切片”slice。使用切片的好处在于每个 reducer 只需要关心自己管理的部分 state而不需要关心整个 state tree 的结构。这样可以使 reducer 的代码更加清晰和易于维护。此外切片还可以让我们更加方便地组织 action creators 和 selectors从而使代码结构更加清晰
九十四、介绍一下createSlice
createSlice是Redux Toolkit中提供的一个用于快速创建Redux reducer的函数。它基于Redux reducer的标准结构使得创建和更新Redux state更加方便、简洁。使用createSlice函数创建的slice包含了一个Redux reducer、一组action creators和对应的action types。
九十五、createSlice解决了什么问题
createSlice 解决了传统 Redux 应用中需要手动编写 reducer 和 action creators 的问题简化了 Redux 应用程序的开发流程提高了代码的可读性和可维护性。
在传统的 Redux 应用程序中我们需要手动编写 reducer 函数来处理每个 action type然后将它们组合成一个大的 rootReducer 函数。这样的代码结构往往比较冗长和繁琐同时也容易出错。而 createSlice 的出现让我们可以通过提供一个包含 reducer 和 action creators 的对象来自动生成相应的 reducer 函数和 action creators从而避免了手动编写 reducer 的过程使得开发者更加专注于业务逻辑的实现。
九十六、Redux Toolkit中如何编写异步代码
使用createAsyncThunk createAsyncThunk 是一个由 Redux Toolkit 提供的函数用于创建处理异步操作的 thunk action creator。使用 createAsyncThunk 可以简化 Redux 中处理异步操作的流程使代码更加清晰、简洁。
九十七、介绍一下redux-toolkit中的createSelector
在 Redux Toolkit 中createSelector 是一个函数它允许我们从 Redux store 中选择部分状态数据并将其作为参数传递给一个 memoized 函数以避免在相同的输入下重复计算。此外Redux Toolkit 中的 createSelector 还支持嵌套选择器和参数选择器使得选择和组合 Redux store 中的状态更加灵活
九十八、createSelector的使用场景
createSelector函数主要用于优化React应用程序中的性能特别是在具有大量数据的情况下。它的主要用途是创建输出选择器函数该函数将redux store中的多个状态组合并到单个值中并将该值缓存以提高性能
1、过滤和排序数据
通过createSelector函数可以根据多个条件从Redux store中选择数据并使用JavaScript函数对其进行过滤、排序等处理。
2、转换数据格式
通过createSelector函数可以将Redux store中的原始数据转换为更易于处理的格式如图表数据饼状图数据等。
3、避免不必要的渲染
使用createSelector函数可以避免不必要的渲染。当createSelector函数的输入参数未更改时将从缓存中返回结果。只有当输入参数更改时createSelector函数才会重新计算其输出并在React组件中触发渲染。
4、避免重复计算
在Redux store中包含大量数据时使用createSelector函数可以避免不必要的计算。例如可以通过创建一个选择器函数该函数选择一个对象数组并返回其长度来避免在每次计算数组长度时进行重复的大量计算