前端常见面试题之react高级特性(Portals、Context、HOC等)

文章目录

  • 1. Portals将子组件渲染到父组件以外的DOM节点上
  • 2. Context组件树中传递数据
  • 3. react中如何加载异步组件
  • 4. shouldComponentUpdate有什么用
  • 5. state中值的不可变性
  • 6. HOC和Render Props代码复用和逻辑分离
  • 7. redux
  • 8.React-Redux
  • 9. react-reducx异步action

1. Portals将子组件渲染到父组件以外的DOM节点上

在React中,Portals是一种机制,可以将子组件渲染到父组件以外的DOM节点上。通常情况下,React组件会被渲染到它们的父组件的DOM结构中,但有时我们希望将某个组件渲染到DOM结构的其他位置或根节点上,这时就可以使用Portals。

Portals的主要用途包括但不限于以下几点:

  1. 在应用中的任意位置渲染组件,无需将所有组件都嵌套在同一个层级结构中。
  2. 在模态框或弹出窗口等场景下,将组件渲染到最顶层的DOM节点上,确保它们处于最前面且不受其他布局影响。
  3. 在复杂布局下,将组件渲染到特定的DOM节点上,实现更灵活的布局控制。

举个例子,假设我们有一个模态框组件Modal,并希望将其渲染到页面的最顶层节点上,可以使用Portals实现:

import { createPortal } from 'react-dom';const modalRoot = document.getElementById('modal-root');class Modal extends React.Component {render() {return createPortal(this.props.children,modalRoot);}
}// 在父组件中使用Modal组件
class App extends React.Component {render() {return (<div><h1>Hello, React!</h1><Modal><div>This is a modal content.</div></Modal></div>);}
}ReactDOM.render(<App />, document.getElementById('app'));

在上面的例子中,Modal组件使用Portals将其子组件渲染到了id为’modal-root’的DOM节点上,而不是直接嵌套在App组件内部。这样就实现了在页面的不同位置渲染组件的效果。

2. Context组件树中传递数据

React中的Context是一种全局状态管理的方法,它允许在组件树中传递数据,而不必一层层地手动传递props。通常用于在组件之间共享一些全局的信息。

Context包括两部分:Provider和Consumer。Provider提供数据并传递给子组件,而Consumer消费Provider提供的数据。

一个简单的例子是,在一个多层嵌套的组件结构中,如果需要在底层组件访问或修改顶层组件的状态数据,使用Context就会很方便。

// 创建一个Context
const MyContext = React.createContext();// 顶层组件提供数据
class ParentComponent extends React.Component {state = {message: 'Hello from ParentComponent!',};render() {return (<MyContext.Provider value={this.state.message}><ChildComponent /></MyContext.Provider>);}
}// 底层组件消费数据
const ChildComponent = () => {return (<MyContext.Consumer>{value => <p>{value}</p>}</MyContext.Consumer>);
}// 渲染
ReactDOM.render(<ParentComponent />, document.getElementById('root'));

在此例中,ParentComponent提供了一个message数据,并通过MyContext.Provider传递给ChildComponent,ChildComponent通过MyContext.Consumer来消费并显示message的值。

在函数式组件中可以使用useContext来实现,具体可以看看这篇react中的context实现深层数据传递

3. react中如何加载异步组件

在 React 中加载异步组件通常使用 React.lazy() 方法。React.lazy() 接收一个函数,这个函数需要动态 import 这个组件,返回一个 Promise,React 将在组件加载完成后渲染它。

举例说明:

import React, { Suspense } from 'react';const AsyncComponent = React.lazy(() => import('./AsyncComponent'));function App() {return (<div><h1>异步组件加载示例</h1><Suspense fallback={<div>Loading...</div>}><AsyncComponent /></Suspense></div>);
}export default App;

在上面的示例中,我们创建了一个 App 组件,通过 React.lazy() 方法加载了一个异步组件 AsyncComponent。在 Suspense 组件中,我们可以设置一个 fallback 属性,在组件加载完成前会显示该属性的内容。

需要注意的是,React.lazy() 目前只支持默认导出的组件,所以被异步加载的组件需要使用默认导出。

4. shouldComponentUpdate有什么用

shouldComponentUpdate是React组件中一个重要的生命周期函数,用于控制组件的更新频率。当组件的props或state发生变化时,React会调用shouldComponentUpdate方法来判断是否需要重新渲染组件。如果shouldComponentUpdate返回false,React则会阻止组件的更新,从而提高性能。

举个例子,假设有一个列表组件List,当用户点击某个按钮时,会向列表中添加一个新的项。但是,如果列表中已经包含相同的项,则不需要更新列表。在这种情况下,我们可以通过shouldComponentUpdate来判断新的项是否已经存在于列表中,如果存在则返回false,否则返回true。

class List extends React.Component {shouldComponentUpdate(nextProps) {// 检查新的项是否已经存在于列表中if (this.props.items.includes(nextProps.newItem)) {return false;}return true;}render() {return (<ul>{this.props.items.map(item => (<li key={item}>{item}</li>))}</ul>);}
}

在上面的例子中,当shouldComponentUpdate返回false时,新的项不会被添加到列表中,从而避免不必要的组件更新。这样可以减少组件的渲染次数,提高性能。

5. state中值的不可变性

在React中,保持原有state值的不可变性是为了确保组件的可预测性和性能。如果直接修改state中的值,可能会导致不可预测的行为,因为React可能无法正确地检测到state的变化,进而无法正确地重新渲染组件。

通过确保state的不可变性,可以减少出现bug的可能性,因为我们可以清晰地追踪state的变化。此外,React使用了一个称为“浅比较”的机制来检测state的变化,如果直接修改state中的值,可能会导致React无法正确地检测到state的变化,导致组件不会重新渲染,从而影响性能

因此,在使用setState更新state时,应该始终创建一个新的对象或数组,而不是直接修改原始state,以确保state的不可变性和组件的可靠性。

举例来说,假设有一个组件的state中有一个数组:

this.state = {numbers: [1, 2, 3, 4, 5]
}

如果我们想将数组中第一个元素改为6,正确的做法是先创建一个新的数组,然后再更新state:

const newNumbers = [...this.state.numbers];
newNumbers[0] = 6;this.setState({numbers: newNumbers});

如果直接修改state中的数组,例如:

this.state.numbers[0] = 6;
this.setState({numbers: this.state.numbers});

这种方式会直接修改原有的state中的数组,可能导致React无法正确检测到state的变化,从而导致界面没有得到正确的更新。

这是因为React在进行state更新时,会进行引用比较来确定是否需要重新渲染组件。如果直接修改state中的数组而没有保持其不可变性,即没有创建一个新的数组来替换原有的数组,React可能会认为引用未发生改变,从而不会触发重新渲染。这样就会导致界面没有得到正确的更新,因为实际上state中的数据已经发生了变化,但React并没有正确检测到这一点。因此,在React中,强调保持原有值的不可变性是为了确保React能够正确地检测到state的变化并触发组件的重新渲染。

因此我们在操作数组时,要使用返回新数组的方法

常用的JS数组方法中,会返回新数组的方法有:

  1. map()
  2. filter()
  3. concat()
  4. slice()
  5. flat()
  6. reduce()

修改原数组的方法有:

  1. pop()
  2. push()
  3. shift()
  4. unshift()
  5. splice()
  6. reverse()
  7. sort()

6. HOC和Render Props代码复用和逻辑分离

HOC(Higher Order Component)和Render Props都是React中常用的模式,用于组件之间的代码复用和逻辑分离。

HOC是一个函数,接受一个组件作为参数并返回一个新的增强组件。通过HOC可以将通用的逻辑封装到一个函数中,然后在多个地方重复使用。例如,下面是一个HOC,用于给组件添加loading状态:

const withLoading = (WrappedComponent) => {return class WithLoading extends React.Component {state = {loading: true}componentDidMount() {setTimeout(() => {this.setState({ loading: false });}, 2000);}render() {return this.state.loading ? <div>Loading...</div> : <WrappedComponent {...this.props} />;}}
}

Render Props则是通过组件的props将函数传递给子组件,子组件可以调用该函数来获取值或者逻辑。通过Render Props可以更加灵活地共享代码逻辑。下面是一个Render Props的示例,用于共享鼠标坐标:

class MouseTracker extends React.Component {state = { x: 0, y: 0 };handleMouseMove = (event) => {this.setState({ x: event.clientX, y: event.clientY });}render() {return (<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>{this.props.children(this.state)}</div>);}
}const App = () => (<MouseTracker>{({ x, y }) => (<div><h1>Mouse coordinates:</h1><p>X: {x}, Y: {y}</p></div>)}</MouseTracker>
);

在上面的例子中,MouseTracker组件通过props.children将函数传递给子组件,子组件可以通过调用该函数获取到鼠标坐标的值。

7. redux

Redux是一个用于JavaScript应用程序的开源状态管理库,它被设计为一个可预测状态容器,使得应用的状态管理更加可控并且易于调试。

Redux的主要用途是管理应用程序的状态,例如用户的登录状态、页面的加载状态以及组件之间的通信等。通过Redux,我们可以将应用程序的状态统一管理在一个容器中,通过定义action、reducer和store来管理数据的流动,从而确保状态的一致性和可控性。

举例说明,我们可以创建一个简单的计数器应用程序来演示Redux的用法:

首先,我们定义一个action,例如增加计数器的动作:

const increment = () => {return {type: 'INCREMENT'};
};

然后,我们定义一个reducer来处理action,并更新应用程序的状态:

const counterReducer = (state = 0, action) => {switch(action.type) {case 'INCREMENT':return state + 1;default:return state;}
};

接着,我们创建一个Redux store,并将reducer注册到store中:

import { createStore } from 'redux';const store = createStore(counterReducer);

最后,在应用程序中,我们可以通过dispatch一个action来更新应用的状态,并通过getState方法获取最新的状态值:

store.dispatch(increment());
console.log(store.getState()); // 输出1

通过以上示例,我们可以看到Redux通过action和reducer来管理应用程序的状态,使得状态的变化变得可预测和容易管理。

Action
Reducer
State
View

8.React-Redux

React-Redux 是一个用于 React 应用程序的 JavaScript 库,用于管理应用程序状态并帮助在组件之间传递数据。React-Redux 绑定了 Redux 的状态管理功能与 React 组件,使得在 React 应用程序中使用 Redux 变得更加简单和高效。

使用 React-Redux 有以下几个主要的优势:

  1. 提供了一个统一的状态管理中心,使得应用程序的状态更加集中化和易于维护。
  2. 可以避免将状态传递多个组件层级,通过 React-Redux 提供的 connect 函数,可以直接从状态管理中心获取数据。
  3. 帮助优化组件的性能,可以避免不必要的渲染和数据更新。

使用 React-Redux 的基本步骤如下:

  1. 安装 React-Redux:在项目中安装 react-redux 包,可以使用 npm 或者 yarn 进行安装。
  2. 创建 Redux store:在项目中创建 Redux store,并定义相关的 reducer、action 和 middleware。
  3. 使用 Provider:在根组件中使用 Provider 组件,并将创建的 Redux store 作为 props 传递给 Provider。
  4. 使用 connect 函数:在需要访问 Redux store 数据的组件中,使用 connect 函数来连接组件和 Redux store,将状态作为 props 传递给组件。

举例说明:

// 定义 reducer
const counterReducer = (state = 0, action) => {switch(action.type) {case 'INCREMENT':return state + 1;case 'DECREMENT':return state - 1;default:return state;}
};// 创建 Redux store
const store = createStore(counterReducer);// 创建 React 组件
const Counter = ({ count, increment, decrement }) => (<div><h1>{count}</h1><button onClick={increment}>Increment</button><button onClick={decrement}>Decrement</button></div>
);// 连接组件和 Redux store
const mapStateToProps = state => ({count: state
});const mapDispatchToProps = dispatch => ({increment: () => dispatch({ type: 'INCREMENT' }),decrement: () => dispatch({ type: 'DECREMENT' })
});const ConnectedCounter = connect(mapStateToProps, mapDispatchToProps)(Counter);// 在根组件中使用 Provider
const App = () => (<Provider store={store}><ConnectedCounter /></Provider>
);

在以上的示例中,我们创建了一个简单的计数器应用程序,使用了 React-Redux 进行状态管理。通过 connect 函数连接了 Counter 组件和 Redux store,并使用 Provider 在根组件中传递了 Redux store。当点击 Increment 和 Decrement 按钮时,会分别触发对应的 action,从而更新组件中的状态。

9. react-reducx异步action

在React-Redux中使用异步action通常需要借助中间件,最常用的中间件是redux-thunk。redux-thunk允许action创建函数返回一个函数而不是一个action对象,这样就可以在返回的函数中进行异步操作。

下面是一个使用redux-thunk的异步action的示例:

// actions.js
const fetchPostsRequest = () => {return {type: 'FETCH_POSTS_REQUEST'}
}const fetchPostsSuccess = (posts) => {return {type: 'FETCH_POSTS_SUCCESS',payload: posts}
}const fetchPostsError = (error) => {return {type: 'FETCH_POSTS_ERROR',payload: error}
}export const fetchPosts = () => {return (dispatch) => {dispatch(fetchPostsRequest());fetch('https://jsonplaceholder.typicode.com/posts').then(response => response.json()).then(data => {dispatch(fetchPostsSuccess(data));}).catch(error => {dispatch(fetchPostsError(error));});}
}// reducer.js
const initialState = {posts: [],loading: false,error: null
}const postsReducer = (state = initialState, action) => {switch (action.type) {case 'FETCH_POSTS_REQUEST':return {...state,loading: true}case 'FETCH_POSTS_SUCCESS':return {...state,loading: false,posts: action.payload,error: null}case 'FETCH_POSTS_ERROR':return {...state,loading: false,error: action.payload}default:return state}
}export default postsReducer;

在上面的示例中,fetchPosts函数是一个异步action创建函数,它返回一个函数,这个函数内部执行异步操作,获取数据后分发相应的成功或失败action。在reducer中根据不同的action类型更新state。

需要注意的是,在创建store时要应用redux-thunk中间件,示例代码如下:

// store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';const store = createStore(rootReducer, applyMiddleware(thunk));export default store;

这样就可以在React组件中dispatch这个fetchPosts函数来获取数据,并在数据获取成功或失败后更新UI。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/698477.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【无标题】//创建单向循环链表//创建结点//头插//按位置插入//尾删//按位置删除

1.h头文件 #ifndef __1_H_ #define __1_H_ #include <stdio.h> #include <stdlib.h> typedef int datatype; typedef struct loop_list {union{int len;datatype data;};struct loop_list *next; }loop_list,*loop_p; loop_p create_head(); loop_p create_node(…

【嵌入式学习】QT-Day3-Qt基础

1> 思维导图 https://lingjun.life/wiki/EmbeddedNote/20QT 2> 完善登录界面 完善对话框&#xff0c;点击登录对话框&#xff0c;如果账号和密码匹配&#xff0c;则弹出信息对话框&#xff0c;给出提示”登录成功“&#xff0c;提供一个Ok按钮&#xff0c;用户点击Ok后…

子查询

Oracle从入门到总裁:https://blog.csdn.net/weixin_67859959/article/details/135209645 子查询 前面我们学过了利用 group by子句可以实现分组的操作&#xff0c;主要的统计函数有&#xff1a;COUNT()、AVG()、SUM()、MAX()、MIN() 并且介绍了分组统计查询的若干限制以及在…

设计模式面试系列-03

1. Java 中如何实现模板方法模式? 举例:去餐厅吃饭,餐厅给我们提供了一个模板就是:看菜单,点菜,吃饭,付款,走人。注意这里“点菜和付款”是不确定的由子类来完成的,其他的则是一个模板。 1、先定义一个模板。把模板中的点菜和付款,让子类来实现。 package com.yoo…

IP 协议

IP 协议 .IP协议格式四位版本号四位首部长度8位服务类型16位总长度16位标识符,3位标志位,13位片偏移8位生存时间TTL8位协议16位首部校验和32位源地址 32位目的地址IP地址的组成特殊的IP地址 . IP协议格式 四位版本号 用来表示IP协议的版本,现有的IP协议只有两个版本,IPv4,IPv6…

comfyui节点编写示例文件(下)

** 1、先看示例文件的结构 ** ** 2、设置输入参数 ** ** 3、节点指定任务、输出啥 ** ** 4、这个节点干了啥&#xff0c;定义函数、输出结果 ** ** 5、多个节点&#xff0c;就多个类 ** ** 6、设置多个入口 ** ** 7、放置 ** 直接把py文件放到 .\Co…

PostgreSQL教程(四):高级特性

一、简介 在之前的章节里我们已经涉及了使用SQL在PostgreSQL中存储和访问数据的基础知识。现在我们将要讨论SQL中一些更高级的特性&#xff0c;这些特性有助于简化管理和防止数据丢失或损坏。最后&#xff0c;我们还将介绍一些PostgreSQL扩展。 本章有时将引用教程&#xff0…

消息中间件之RocketMQ为什么写文件这么快?

RocketMQ的存储涉及中&#xff0c;很大一部分是基于Kafka的涉及进行优化的。 PageCache 现代操作系统内核被设计为按照Page读取文件&#xff0c;每个Page默认4KB, 因为程序一般符合局部性原理&#xff0c;所以操作系统在读取一段文件内容时&#xff0c;会将该段内容和附件的文…

Nginx网络服务二-----(虚拟机和location)

一、HTTP设置 1.设置虚拟主机 1.1Nginx 基于域名---虚拟主机 include /apps/nginx/conf.d/*.conf; 1.2Nginx 基于端口---虚拟主机 在做了域名的基础上&#xff0c;按照以下步骤继续 1.3Nginx 基于IP---虚拟主机 2.server下的root root路径格式 指定文件的路径 url …

DataX学习详解

参考视频 02_DataX_概述_哔哩哔哩_bilibili 参考文档 大数据技术之DataX-阿里云开发者社区 介绍 DataX 是阿里巴巴开源的一个 异构数据源 离线 同步工具 致力于实现包括关系型数据库(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP&#xff0c;MongDB等各种异构数据源之间…

[python][whl]PyOpenGL轮子文件whl格式所有版本含python3.12版本下载地址汇总

以下PyOpenGL都是windows x64系统使用pyopengl注意对应版本下载&#xff0c;文件名命名规则为&#xff0c;以PyOpenGL-3.1.6-cp312-cp312-win-amd64.whl为例子 PyOpenGL&#xff1a;python模块名称 3.1.6&#xff1a;pyopengl版本号 cp312:表示支持python3.12版本不支持其他…

五、深入学习TensorRT,Developer Guide篇(四)

上一篇文章我们介绍了C的API&#xff0c;这篇文章我们主要针对的是Python的API&#xff0c;起始C和Python在整体流程上面基本一致&#xff0c;但是由于Python天然的简洁性和易用性&#xff0c;Python的API相对来讲还是比较简单的&#xff0c;我们一起来看一下吧。 文章目录 4.…

windows下快速安装nginx 并配置开机自启动

1、下载地址&#xff1a;http://nginx.org/en/download.html 2、启动nginx 注意⚠️ 不要直接双击nginx.exe&#xff0c;这样会导致修改配置后重启、停止nginx无效&#xff0c;需要手动关闭任务管理器内的所有nginx进程。 在nginx.exe目录&#xff0c;打开命令行工具&#xf…

【springblade】springblade(bladeX) 数据权限失效原因分析

文章目录 数据权限接口权限 前言&#xff1a;最近博主在按照bladeX官方文档 配置数据权限 结果发现失效了&#xff0c;网上搜了一下没找到合适的答案&#xff0c;本着求人不如求己的精神&#xff0c;自己调试了一下发现了问题所在&#xff0c;也大致看了一下bladeX的权限逻辑。…

代码随想录算法训练营29期Day56|LeetCode 300,674,718

文档讲解&#xff1a;最长递增子序列 最长连续递增序列 最长重复子数组 300.最长递增子序列 题目链接&#xff1a;https://leetcode.cn/problems/longest-increasing-subsequence/description/ 思路&#xff1a; 设dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长…

unity——shader入门知识点 学习笔记【个人复习向/侵删/有不足之处欢迎斧正】

零、不同图形接口程序对Shader开发的影响&#xff1a; 1.渲染管线(流水线)和图形接口程序的关系&#xff1a;图形接口程序(OpenGL、 DX等)提供了对渲染管线(流水线)的控制和管理功能&#xff0c;它是开发者和硬件打交道的中间层 2. Shader和图形接口程序的关系&#xf…

计算机毕业设计 基于SpringBoot的宠物商城网站系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

LeetCode206反转链表

LeetCode206,直接看下面链接的动画演示部分就行了,就能快速理解pre和cur的作用 代码随想录:反转链表 这个是另外的在B站的视频讲解链接 【帮你拿下反转链表 | LeetCode&#xff1a;206.反转链表 | 双指针法 | 递归法】 https://www.bilibili.com/video/BV1nB4y1i7eL/?share_so…

Linux理解

VMware安装Linux安装 目录 VMware安装Linux安装 1.1 什么是Linux 1.2 为什么要学Linux 1.3 学完Linux能干什么 2.1 主流操作系统 2.2 Linux系统版本 VMware安装Linux安装 1.1 什么是Linux Linux是一套免费使用和自由传播的操作系统。 1.2 为什么要学Linux 1). 企业用人…

Python3获取办公室IP并更新至腾讯云安全组

需求来源&#xff1a; 由于云服务器的安全组限制了只能在公司IP下远程连接&#xff0c;每次更新安全组时只能通过手动更新&#xff0c;办公室IP是动态IP&#xff0c;每隔三天变一次IP&#xff0c;时间久了就觉得麻烦&#xff0c;所以在查阅了腾讯云文档后发现有安全组的SDK&…