React 面试问题🔥
你想转行做更赚钱的工作吗?或者你已经投入了大量的时间和精力准备周末的面试?你知道有多少人在面试中被拒之门外,是因为他们只准备概念,而没有专注于真正会被问到的问题吗?这次,避免成为那样的人。这是你能找到的最全面的 React JS 面试题集。它包含大量经常被问到的关键 React JS 面试题和答案。无论是应届毕业生、经验丰富的专业人士、高级开发人员还是测试人员,都能从这些题型中受益,这些问题不仅涵盖了 React JS 的基础知识,还涵盖了最先进、最具挑战性的问题。这篇博文将为那些想要练习和提升 React.js 技能的人提供全面的指导。我建议你事先仔细阅读所有内容,并练习和巩固你的 React 知识。
React 基础知识
1.元素和组件有什么区别?
元素是一个普通的对象,它描述了你希望在屏幕上显示的内容(DOM 节点或其他组件)。元素可以在其 props 中包含其他元素。创建 React 元素的成本很低。元素一旦创建,就永远不会改变。React 元素的对象表示如下:
const element = React.createElement(
'div',
{id: 'login-btn'},
'Login'
)
上面的 React.createElement() 函数返回一个对象:
{
type: 'div',
props: {
children: 'Login',
id: 'login-btn'
}
}
最后,它使用 ReactDOM.render() 渲染到 DOM:
<div id='login-btn'>Login</div>
组件可以用几种不同的方式声明。它可以是一个带有 render() 方法的类,也可以定义为一个函数。无论哪种情况,它都接受 props 作为输入,并返回一个 JSX 树作为输出:
const Button = ({ onLogin }) =>
<div id={'login-btn'} onClick={onLogin}>Login</div>
然后 JSX 被转换为 React.createElement() 函数树:
const Button = ({ onLogin }) => React.createElement(
'div',
{ id: 'login-btn', onClick: onLogin },
'Login'
)
2. 如何在 React 中创建组件?
有两种可能的方法可以创建组件。
- 函数组件:这是创建组件最简单的方法。这些是纯 JavaScript 函数,接受 props 对象作为第一个参数,并返回 React 元素:
function Greeting({ message }) {
return <h1>{`Hello, ${message}`}</h1>
}
- 类组件:你也可以使用 ES6 类来定义组件。上面的函数组件可以写成:
class Greeting extends React.Component {
render() {
return <h1>{`Hello, ${this.props.message}`}</h1>
}
}
3.什么是纯组件?
React.PureComponent 与 React.Component 完全相同,只是它能帮你处理 shouldComponentUpdate() 方法。当 props 或 state 发生变化时,PureComponent 会对 props 和 state 进行浅比较。而组件则不会将当前的 props 和 state 与下一个 props 和 state 进行比较。因此,每当 shouldComponentUpdate 被调用时,组件都会默认重新渲染。
4. React 中的状态是什么?
组件的状态是一个对象,它保存着一些可能在组件生命周期内发生变化的信息。我们应该始终尝试使状态尽可能简单,并尽量减少有状态组件的数量。让我们创建一个带有消息状态的用户组件,
class User extends React.Component {
constructor(props) {
super(props)
this.state = {
message: 'Welcome to React world'
}
}
render() {
return (
<div>
<h1>{this.state.message}</h1>
</div>
)
}
}
状态类似于道具,但它是私有的并且完全由组件控制。即,在所有者组件决定传递它之前,任何其他组件都无法访问它。
- React 中的 props 是什么?
Props 是组件的输入。它们是单个值或包含一组值的对象,这些值在创建时使用类似于 HTML 标签属性的命名约定传递给组件。它们是从父组件向下传递到子组件的数据。React 中 props 的主要目的是提供以下组件功能:
-
将自定义数据传递给您的组件。
-
触发状态改变。
-
通过组件 render() 方法中的 this.props.reactProp 使用
例如,让我们创建一个具有 reactProp 属性的元素:
<Element reactProp={'1'} />
这个 react pro(或者任何你想出的名字)名称将成为附加到 React 原生 props 对象的属性,该属性最初已经存在于使用 React 库创建的所有组件上。
props.reactProp
6.state和props有什么区别?
props 和 state 都是普通的 JavaScript 对象。虽然它们都包含影响渲染输出的信息,但它们在组件方面的功能有所不同。props 传递给组件的方式类似于函数参数,而 state 则在组件内部进行管理,类似于函数内声明的变量。
7.为什么我们不应该直接更新状态?
如果您尝试直接更新状态,那么它将不会重新渲染组件。
//Wrong
this.state.message = 'Hello world'
相反,请使用 setState() 方法。它会安排组件状态对象的更新。当状态发生变化时,组件会通过重新渲染来响应。
//Correct
this.setState({ message: 'Hello World' })
注意:您可以在构造函数中或使用最新的 javascript 的类字段声明语法直接分配给状态对象。
8. 回调函数作为 setState() 的参数有什么用途?
当 setState 完成且组件渲染完成后,会调用回调函数。由于 setState() 是异步的,因此回调函数可用于任何后续操作。
注意:建议使用生命周期方法,而不是此回调函数。
setState({ name: 'John' }, () => console.log('The name has updated and component re-rendered'))
9.HTML 和 React 事件处理有什么区别?
以下是 HTML 和 React 事件处理之间的一些主要区别:
- 在 HTML 中,事件名称通常以小写表示,这是惯例:
<button onClick={activateLasers}>
而在 React 中,它遵循驼峰命名法
<button onClick={activateLasers}>
- 在 HTML 中,您可以返回 false 来阻止默认行为。
<a href='#' onclick='console.log("The link was clicked."); return false;' />
而在 React 中你必须明确调用 preventDefault():
function handleClick(event) {
event.preventDefault()
console.log('The link was clicked.')}
- 在 HTML 中,您需要通过附加 () 来调用该函数,而在 React 中,您不应该在函数名称后附加 ()。(例如,参考第一点中的“activateLasers”函数)
10. 如何在 JSX 回调中绑定方法或事件处理程序?
有 3 种可能的方法可以实现此目的:
构造函数中的绑定:在 JavaScript 类中,方法默认不绑定。同样适用于定义为类方法的 React 事件处理程序。通常我们在构造函数中绑定它们。
class Foo extends Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={this.handleClick}>Click Me</button>;
}
}
公共类字段语法:如果您不喜欢使用绑定方法,那么可以使用公共类字段语法来正确绑定回调。
handleClick = () => {
console.log('this is:', this)
}
<button onClick={this.handleClick}>
{'Click me'}
</button>
回调中的箭头函数:您可以在回调中直接使用箭头函数。
handleClick() {
console.log('Click happened');
}
render() {
return <button onClick={() => this.handleClick()}>Click Me</button>;
}
注意:如果将回调作为 prop 传递给子组件,这些组件可能会进行额外的重新渲染。在这种情况下,考虑到性能,建议使用 .bind() 或 public class fields 语法。
11. React 中的合成事件是什么?
SyntheticEvent 是一个跨浏览器的原生事件包装器。它的 API 与浏览器原生事件相同,包括 stopPropagation() 和 preventDefault(),但这些事件在所有浏览器中的工作方式相同。
12.什么是“key”属性,在元素数组中使用它有什么好处?
键是一种特殊的字符串属性,在创建元素数组时应包含该属性。Keyprop 可帮助 React 识别哪些项目已更改、已添加或已删除。我们通常使用数据中的 ID 作为键:
const todoItems = todos.map((todo) =>
<li key={todo.id}>
{todo.text}
</li>
)
当你没有渲染项目的稳定 ID 时,你可以使用 itemindex 作为键作为最后的手段:
13. React 中的 Lifting State Up 是什么?
当多个组件需要共享相同的变化数据时,建议将共享状态提升到它们最近的共同祖先组件。这意味着,如果两个子组件共享来自其父组件的相同数据,则将状态移动到父组件,而不是在两个子组件中都维护本地状态。
14. 组件生命周期有哪些不同的阶段?
组件生命周期有三个不同的生命周期阶段:
-
挂载:组件已准备好挂载到浏览器 DOM 中。此阶段涵盖从构造函数 ()、getDerivedStateFromProps()、render() 和 componentDidMount() 生命周期方法进行的初始化。
-
更新:在此阶段,组件通过两种方式更新:发送新的 props 以及通过 setState() 或 forceUpdate() 更新状态。此阶段涵盖 getDerivedStateFromProps()、shouldComponentUpdate()、render()、getSnapshotBeforeUpdate() 和 componentDidUpdate() 生命周期方法。
-
卸载:在最后一个阶段,组件不再需要,并从浏览器 DOM 中卸载。此阶段包含 componentWillUnmount() 生命周期方法。
值得一提的是,React 内部在对 DOM 进行更改时有一个阶段的概念。它们划分如下:
-
渲染:组件将渲染,且不会产生任何副作用。这适用于纯组件,并且在此阶段,React 可以暂停、中止或重新启动渲染。
-
预提交在组件实际将更改应用于 DOM 之前,有一个时刻允许 React 通过 getSnapshotBeforeUpdate() 从 DOM 读取。
-
Commit React 与 DOM 协同工作并分别执行最后的生命周期:componentDidMount() 用于挂载,componentDidUpdate() 用于更新,componentWillUnmount() 用于卸载。
15. React 中的门户是什么?
Portal 是一种推荐的方法,用于将子项渲染到父组件 DOM 层次结构之外的 DOM 节点中。
ReactDOM.createPortal(child, container)
第一个参数是任何可渲染的 React 子元素,例如元素、字符串或片段。第二个参数是 DOM 元素。
16.什么是无状态组件?
如果行为独立于其状态,那么它可以是无状态组件。您可以使用函数或类来创建无状态组件。但除非您需要在组件中使用生命周期钩子,否则您应该选择函数组件。如果您决定在这里使用函数组件,会有很多好处;它们易于编写、理解和测试,速度更快,而且您可以完全避免使用这个关键字。
17.初始状态下使用props会发生什么?
如果组件上的 props 在组件未刷新的情况下发生变化,则新的 props 值将永远不会显示,因为构造函数永远不会更新组件的当前状态。props 的状态初始化仅在组件首次创建时运行。以下组件不会显示更新后的输入值:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
records: [],
inputValue: this.props.inputValue
};
}
render() {
return <div>{this.state.inputValue}</div>
}
}
在 render 方法中使用 props 将更新值:
class MyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {
record: []
}
}
render() {
return <div>{this.props.inputValue}</div>
}
}
React 路由器
18. history 的 push() 和 replace() 方法的用途是什么?
历史实例有两种用于导航的方法。
如果您将历史记录视为访问过位置的数组,则 push() 将向数组中添加一个新位置,而 replace() 将用新位置替换数组中的当前位置。
19.如何使用 React Router 以编程方式进行导航?
有三种不同的方法可以在组件内实现编程路由/导航。
使用 withRouter() 高阶函数:withRouter() 高阶函数会将 history 对象作为组件的 prop 注入。该对象提供了 push() 和 replace() 方法,以避免使用 context。
import { withRouter } from 'react-router-dom' // this also works with 'react-router-native'
const Button = withRouter(({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
{'Click Me!'}
</button>
))
使用组件和渲染道具模式:组件传递与 withRouter() 相同的道具,因此您将能够通过历史道具访问历史方法。
import { Route } from 'react-router-dom'
const Button = () => (
<Route render={({ history }) => (
<button
type='button'
onClick={() => { history.push('/new-location') }}
>
{'Click Me!'}
</button>
)} />
)
使用上下文:不推荐使用此选项,并将其视为不稳定的 API。
const Button = (props, context) => (
<button
type='button'
onClick={() => {
context.history.push('/new-location')
}} >
{'Click Me!'}
</button>
)
Button.contextTypes = {
history: React.PropTypes.shape({
push: React.PropTypes.func.isRequired
})
}
20. 如何在 React Router v4 中获取查询参数?
React Router v4 取消了解析查询字符串的功能,因为多年来用户一直要求支持不同的实现。因此,用户可以根据自己的喜好选择实现。推荐使用查询字符串库。
const queryString = require('query-string');
const parsed = queryString.parse(props.location.search);
如果您想要一些原生的东西,您也可以使用 URLSearchParams:
const params = new URLSearchParams(props.location.search)
const foo = params.get('name')
您应该使用适用于 IE11 的应用填充。
React Redux
21.什么是 Redux 选择器以及为什么要使用它们?
选择器是将 Redux 状态作为参数并返回一些数据传递给组件的函数。例如,要从状态中获取用户详细信息:
const getUserData = state => state.user.data
这些选择器有两个主要优点,
选择器可以计算派生数据,允许 Redux 存储最小可能状态
除非其中一个参数发生变化,否则选择器不会重新计算
22. mapDispatchToProps() 有哪些不同的编写方式?
有几种方法可以将动作创建者绑定到 mapDispatchToProps() 中的 dispatch()。以下是可能的选项:
const mapDispatchToProps = (dispatch) => ({
action: () => dispatch(action())
})
const mapDispatchToProps = (dispatch) => ({
action: bindActionCreators(actioimport { ADD_TODO } from './actionTypes'
export default (state = [], action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
{
text: action.text,
completed: false
}
];
default:
return state
}
}
n, dispatch)
})
const mapDispatchToProps = { action }
第三个选项只是第一个选项的简写。
23. React Redux 中的组件和容器有什么区别?
组件是一个类或函数组件,用于描述应用程序的展示部分。容器是一个非正式术语,指的是连接到 Redux Store 的组件。容器订阅 Redux 状态更新并调度操作,它们通常不渲染 DOM 元素;而是将渲染委托给展示子组件。
24.redux-saga 的心理模型是什么?
Saga 就像应用程序中的一个单独线程,它完全负责副作用。redux-saga 是一个 redux 中间件,这意味着这个线程可以通过正常的 Redux 操作从主应用程序启动、暂停和取消,它可以访问完整的 Redux 应用程序状态,也可以分派 Redux 操作。
25.redux-saga 中的 call() 和 put() 有什么区别?
call() 和 put() 都是效果创建函数。call() 函数用于创建效果描述,指示中间件调用承诺。put() 函数创建一个效果,指示中间件将动作分派到存储。让我们举例说明这些效果如何用于获取特定的用户数据。
function* fetchUserSaga(action) {
// `call` function accepts rest arguments, which will be passed to `api.fetchUser` function.
// Instructing middleware to call promise, it resolved value will be assigned to `userData` variable
const userData = yield call(api.fetchUser, action.userId)
// Instructing middleware to dispatch corresponding action.
yield put({
type: 'FETCH_USER_SUCCESS',
userData
})
}
26.什么是Redux Thunk?
Redux Thunk 中间件允许您编写返回函数而非动作的 action creator。Thunk 可用于延迟 action 的调度,或仅在满足特定条件时调度。内部函数接收 store 方法 dispatch() 和 getState() 作为参数。
27.什么是 Redux 选择器以及为什么要使用它们?
选择器是将 Redux 状态作为参数并返回一些数据传递给组件的函数。例如,要从状态中获取用户详细信息:
const getUserData = state => state.user.data
这些选择器有两个主要优点,
选择器可以计算派生数据,允许 Redux 存储最小可能状态
除非其中一个参数发生变化,否则选择器不会重新计算
28.什么是差异算法?
React 需要使用算法来找出如何高效地更新 UI 以匹配最新的树。Diffing 算法旨在生成将一棵树转换为另一棵树所需的最少操作数。然而,这些算法的复杂度约为 O(n3),其中 n 是树中元素的数量。在这种情况下,显示 1000 个元素需要进行大约 10 亿次比较。这过于昂贵。因此,React 基于两个假设实现了启发式 O(n) 算法:
- 两种不同类型的元素将产生不同的树。
- 开发人员可以使用关键属性暗示哪些子元素可能在不同的渲染中保持稳定。
29. 对于 render props 来说,prop 必须命名为 render 吗?
尽管该模式名为“render props”,但你不必使用名为 render 的 prop 来使用此模式。也就是说,任何组件用来判断需要渲染什么的函数 prop,从技术上来说都属于“render prop”。让我们以 children prop 为例,
<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}</Mouse>children={mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}/>
实际上,children 属性不需要在 JSX 元素的“属性”列表中命名。相反,你可以直接将其放在元素内部,
<<Mouse>
{mouse => (
<p>The mouse position is {mouse.x}, {mouse.y}</p>
)}</Mouse>
在使用上述技术(没有任何名称)时,明确指出 children 应该是 propTypes 中的一个函数。
Mouse.propTypes = {
children: PropTypes.func.isRequired
};
30. 使用纯组件的render props 有什么问题?
如果在 render 方法中创建一个函数,则会违背纯组件的初衷。因为浅层 prop 比较对于新的 props 总是会返回 false,而在这种情况下,每次渲染都会为 render props 生成一个新值。你可以通过将 render 函数定义为实例方法来解决这个问题。
31.如何使用渲染道具创建 HOC?
您可以使用带有 render prop 的常规组件来实现大多数高阶组件 (HOC)。例如,如果您更喜欢使用带有鼠标的 HOC 而不是组件,则可以使用带有 render prop 的常规组件轻松创建一个。
function withMouse(Component) {
return class extends React.Component {
render() {
return (
<Mouse render={mouse => (
<Component {...this.props} mouse={mouse} />
)}/>
);
}
}
}
这种渲染道具的方式可以灵活地使用任一模式。
32.什么是开窗技术?
窗口化是一种在给定时间内仅渲染一小部分行的技术,可以显著减少重新渲染组件所需的时间以及创建的 DOM 节点数量。如果您的应用程序需要渲染长列表数据,则建议使用此技术。react-window 和 react-virtualized 都是流行的窗口化库,它们提供了一些可复用的组件来显示列表、网格和表格数据。
33.门户的典型用例是什么?
当父组件具有 overflow: hidden 或影响堆叠上下文的属性(z-index、position、opacity 等样式),并且你需要在视觉上“突破”其容器时,React Portal 非常有用。
例如,对话框、全局消息通知、悬停卡片和工具提示。
34.如何设置不受控组件的默认值?
在 React 中,表单元素的 value 属性会覆盖 DOM 中的值。对于非受控组件,你可能希望 React 指定初始值,但后续更新不受控制。为了处理这种情况,你可以指定 defaultValue 属性来代替 value 属性。
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
User Name:
<input
defaultValue="John"
type="text"
ref={this.input} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
同样适用于选择和文本区域输入。但对于复选框和单选输入,您需要使用默认的“已选中”状态。
由于字符限制,我们无法添加所有 300 多个完整的问题列表,因此我们在下面的链接中创建了一个完全免费的电子书供您下载。
点击此处完全免费下载
这本电子书将为任何希望练习和提升 React.js 技能的人提供详细的指导。我强烈建议您提前仔细阅读所有内容,并练习和提升您的 React 技能。这是目前最全面的 React JS 面试题库。它包含许多必备且常见的 React JS 面试题和答案。无论是新手、经验丰富的专业人士、高级开发人员还是测试人员,都能从本书的广泛问题中受益,这些问题不仅涵盖了 React JS 的基础知识,还涵盖了最高级的问题。干杯!祝您编程愉快,好运连连!
加入我们的Discord社区!!
进一步阅读
-
https://www.edureka.co/blog/interview-questions/react-interview-questions/
-
https://www.simplilearn.com/tutorials/reactjs-tutorial/reactjs-interview-questions
-
https://www.fullstack.cafe/blog/react-js-interview-questions
-
https://gist.github.com/vishnu-saini/d60fdde3a59e42368b447dc2c712fdf9