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;