React常见面试题

以下是一些React常见面试题及其对应的实例代码。这些示例涵盖了从基础概念到高级应用的多个方面。

1. React组件的生命周期

问题: 请描述React组件的生命周期,并给出每个阶段的一个代码示例。

答案:

jsx复制代码
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
console.log('Constructor');
this.state = {
data: null,
};
}
static getDerivedStateFromProps(nextProps, prevState) {
console.log('getDerivedStateFromProps');
// 返回一个新的state对象或者null
return null;
}
componentDidMount() {
console.log('Component Did Mount');
// 可以进行DOM操作,网络请求等
fetch('/api/data')
.then(response => response.json())
.then(data => this.setState({ data }));
}
shouldComponentUpdate(nextProps, nextState) {
console.log('Should Component Update');
// 返回true或者false,决定是否更新组件
return true;
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('Get Snapshot Before Update');
// 在DOM更新前获取一些信息(比如滚动位置)
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('Component Did Update');
// DOM更新后可以进行操作
}
componentWillUnmount() {
console.log('Component Will Unmount');
// 组件即将卸载,进行清理操作(比如取消网络请求,清理定时器)
}
render() {
console.log('Render');
return (
<div>
{this.state.data ? (
<div>{this.state.data.text}</div>
) : (
<div>Loading...</div>
)}
</div>
);
}
}
export default MyComponent;

2. React Hooks

问题: 请用React Hooks实现一个计数器组件。

答案:

jsx复制代码
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
}
export default Counter;

3. React Router

问题: 请使用React Router实现一个简单的路由系统,包含主页和关于页面。

答案:

jsx复制代码
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = () => <div>Home Page</div>;
const About = () => <div>About Page</div>;
function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<a href="#" onClick={() => window.location.href = "/"}>Home</a>
</li>
<li>
<a href="#" onClick={() => window.location.href = "/about"}>About</a>
</li>
</ul>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</div>
</Router>
);
}
export default App;

4. Redux

问题: 请描述Redux的工作流程,并展示如何在React中使用Redux。

答案:

Redux Store

javascript复制代码
// store.js
import { createStore } from 'redux';
const initialState = {
count: 0,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1,
};
case 'DECREMENT':
return {
...state,
count: state.count - 1,
};
default:
return state;
}
};
const store = createStore(reducer);
export default store;

Provider和Connect

jsx复制代码
// App.js
import React from 'react';
import { Provider, useSelector, useDispatch } from 'react-redux';
import store from './store';
const Counter = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();
const increment = () => {
dispatch({ type: 'INCREMENT' });
};
const decrement = () => {
dispatch({ type: 'DECREMENT' });
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
function App() {
return (
<Provider store={store}>
<div>
<h1>Redux Counter</h1>
<Counter />
</div>
</Provider>
);
}
export default App;

5. Context API

问题: 请使用Context API创建一个简单的全局状态管理。

答案:

jsx复制代码
// MyContext.js
import React, { createContext, useState } from 'react';
export const MyContext = createContext();
export const MyProvider = ({ children }) => {
const [count, setCount] = useState(0);
return (
<MyContext.Provider value={{ count, setCount }}>
{children}
</MyContext.Provider>
);
};
// App.js
import React, { useContext } from 'react';
import { MyContext, MyProvider } from './MyContext';
const Counter = () => {
const { count, setCount } = useContext(MyContext);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
function App() {
return (
<MyProvider>
<div>
<h1>Context API Counter</h1>
<Counter />
</div>
</MyProvider>
);
}
export default App;

6. 状态提升(Lifting State Up)

问题: 当多个组件需要共享同一个状态时,如何在它们之间共享状态?

解决方案: 使用状态提升,即将状态提升到最近的共同父组件中,并通过 props 传递给子组件。

实例代码:

jsx复制代码
import React, { useState } from 'react';
function ParentComponent() {
const [value, setValue] = useState('');
return (
<div>
<h1>Parent Component</h1>
<ChildComponent1 value={value} onChange={setValue} />
<ChildComponent2 value={value} />
</div>
);
}
function ChildComponent1({ value, onChange }) {
return (
<div>
<h2>Child Component 1</h2>
<input type="text" value={value} onChange={(e) => onChange(e.target.value)} />
</div>
);
}
function ChildComponent2({ value }) {
return (
<div>
<h2>Child Component 2</h2>
<p>Value: {value}</p>
</div>
);
}
export default ParentComponent;

7. 性能优化(Memoization and shouldComponentUpdate)

问题: 当父组件重新渲染时,如何避免不必要的子组件重新渲染?

解决方案: 使用 React.memo 对函数组件进行记忆化,或使用类组件中的 shouldComponentUpdate 方法。

实例代码(使用 React.memo):

jsx复制代码
import React, { useState, memo } from 'react';
const ExpensiveComponent = memo(({ value }) => {
console.log('ExpensiveComponent rendered');
return (
<div>
<p>Value: {value}</p>
</div>
);
});
function ParentComponent() {
const [count, setCount] = useState(0);
const [value, setValue] = useState('');
return (
<div>
<h1>Parent Component</h1>
<button onClick={() => setCount(count + 1)}>Increment Count {count}</button>
<input type="text" value={value} onChange={(e) => setValue(e.target.value)} />
<ExpensiveComponent value={value} />
</div>
);
}
export default ParentComponent;

8. 条件渲染

问题: 如何根据条件来渲染不同的 UI 元素?

解决方案: 使用 JavaScript 的条件(if)语句或三元运算符。

实例代码:

jsx复制代码
import React, { useState } from 'react';
function ConditionalRenderingComponent() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<div>
<h1>Conditional Rendering Example</h1>
{isLoggedIn ? (
<div>
<p>Welcome, user!</p>
<button onClick={() => setIsLoggedIn(false)}>Logout</button>
</div>
) : (
<div>
<p>Please log in.</p>
<button onClick={() => setIsLoggedIn(true)}>Login</button>
</div>
)}
</div>
);
}
export default ConditionalRenderingComponent;

9. 事件处理

问题: 如何处理 React 中的事件?

解决方案: 在 JSX 中使用驼峰命名法的事件处理函数,例如 onClick 而不是 onclick

实例代码:

jsx复制代码
import React, { useState } from 'react';
function EventHandlingComponent() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<h1>Event Handling Example</h1>
<p>You clicked {count} times</p>
<button onClick={handleClick}>Click me</button>
</div>
);
}
export default EventHandlingComponent;

10. 列表渲染

问题: 如何渲染一个列表?

解决方案: 使用数组的 map 方法来遍历数据并生成 JSX 元素。

实例代码:

jsx复制代码
import React from 'react';
const items = ['Item 1', 'Item 2', 'Item 3', 'Item 4'];
function ListRenderingComponent() {
return (
<div>
<h1>List Rendering Example</h1>
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default ListRenderingComponent;

11. 表单处理

问题: 如何处理 React 中的表单输入和提交?

解决方案: 使用受控组件(controlled components),即表单数据由 React 组件的状态(state)管理。

实例代码:

jsx复制代码
import React, { useState } from 'react';
function FormHandlingComponent() {
const [formData, setFormData] = useState({ username: '', email: '' });
const handleChange = (e) => {
const { name, value } = e.target;
setFormData({
...formData,
[name]: value
});
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('Form Submitted:', formData);
// 在这里可以添加表单提交的逻辑,如发送数据到服务器
};
return (
<div>
<h1>Form Handling Example</h1>
<form onSubmit={handleSubmit}>
<div>
<label>Username:</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
/>
</div>
<div>
<label>Email:</label>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
/>
</div>
<button type="submit">Submit</button>
</form>
</div>
);
}
export default FormHandlingComponent;

12. 组件生命周期

问题: 如何在组件的不同生命周期阶段执行代码?

解决方案: 在类组件中使用 componentDidMount, componentDidUpdate, componentWillUnmount 等生命周期方法。在函数组件中,使用 useEffect 钩子。

实例代码(类组件):

jsx复制代码
import React, { Component } from 'react';
class LifecycleComponent extends Component {
constructor(props) {
super(props);
this.state = { data: null };
}
componentDidMount() {
// 组件挂载后执行
console.log('Component Did Mount');
// 模拟异步数据获取
setTimeout(() => {
this.setState({ data: 'Fetched Data' });
}, 1000);
}
componentDidUpdate(prevProps, prevState) {
// 组件更新后执行
if (prevState.data !== this.state.data) {
console.log('Component Did Update', this.state.data);
}
}
componentWillUnmount() {
// 组件卸载前执行
console.log('Component Will Unmount');
}
render() {
return (
<div>
<h1>Lifecycle Example</h1>
{this.state.data ? <p>{this.state.data}</p> : <p>Loading...</p>}
</div>
);
}
}
export default LifecycleComponent;

实例代码(函数组件使用 useEffect):

jsx复制代码
import React, { useState, useEffect } from 'react';
function LifecycleFunctionComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// 组件挂载和更新时执行
console.log('Component Did Mount or Update');
// 模拟异步数据获取
setTimeout(() => {
setData('Fetched Data');
}, 1000);
// 清理函数,组件卸载时执行
return () => {
console.log('Cleanup on Component Unmount');
};
}, []); // 空数组作为第二个参数表示这个 effect 只在挂载和卸载时运行一次
return (
<div>
<h1>Lifecycle Function Component Example</h1>
{data ? <p>{data}</p> : <p>Loading...</p>}
</div>
);
}
export default LifecycleFunctionComponent;

13. 错误边界(Error Boundaries)

问题: 如何捕获和处理 React 组件树中的 JavaScript 错误?

解决方案: 使用类组件创建错误边界组件,该组件可以捕获其子组件树中的渲染过程、生命周期方法和构造函数中的错误,并显示降级 UI 而不是崩溃整个应用。

实例代码:

jsx复制代码
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
// 更新 state 以触发降级 UI
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
// 你可以将错误日志上报给服务器
console.error('Uncaught error:', error, errorInfo);
}
render() {
if (this.state.hasError) {
// 你可以自定义降级后的 UI
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
function BrokenComponent() {
throw new Error('I am a broken component!');
}
function App() {
return (
<ErrorBoundary>
<div>
<h1>Error Boundary Example</h1>
<BrokenComponent />
</div>
</ErrorBoundary>
);
}
export default App;