《公司拖欠工资的第N天,我整理了这份硬核React面试题清单,助我绝地反击》

192 阅读4分钟

React面试题目详解:从入门到精通,这些题你都会吗?

前言

最近在准备React相关的面试,整理了一些高频面试题,分享给大家。这些题目涵盖了React的基础概念、生命周期、Hooks、性能优化等各个方面,希望能帮助到正在准备面试的小伙伴们!

目录


基础概念

1. React是什么?它的核心特性有哪些?

答案: React是一个用于构建用户界面的JavaScript库,由Facebook开发。

核心特性:

  • 组件化:将UI拆分为独立、可复用的组件
  • 虚拟DOM:通过虚拟DOM提高渲染性能
  • 单向数据流:数据从父组件流向子组件
  • JSX:允许在JavaScript中编写HTML样式的代码

详细解析: React的核心思想是组件化开发,每个组件都有自己的状态和生命周期。虚拟DOM是React性能优化的关键,它会在内存中维护一个虚拟的DOM树,通过diff算法找出需要更新的部分,然后批量更新真实DOM。

2. 什么是JSX?它和HTML有什么区别?

答案: JSX是JavaScript XML的缩写,是React中用来描述UI的语法糖。

主要区别:

  • JSX中的属性使用驼峰命名(如classNameonClick
  • JSX中的事件处理使用函数引用而不是字符串
  • JSX中可以嵌入JavaScript表达式
  • JSX最终会被编译为React.createElement()调用

代码示例:

// JSX写法
const element = (
  <div className="container">
    <h1>Hello, {name}!</h1>
    <button onClick={handleClick}>Click me</button>
  </div>
);

// 编译后的JavaScript
const element = React.createElement(
  'div',
  { className: 'container' },
  React.createElement('h1', null, 'Hello, ', name, '!'),
  React.createElement('button', { onClick: handleClick }, 'Click me')
);

生命周期

3. React 16.8+的生命周期有哪些?请详细说明

答案: React 16.8+引入了Hooks,但类组件仍然保留原有的生命周期。

挂载阶段:

  • constructor():构造函数,初始化state和绑定事件
  • getDerivedStateFromProps():从props派生state
  • render():渲染组件
  • componentDidMount():组件挂载完成

更新阶段:

  • getDerivedStateFromProps():props或state变化时调用
  • shouldComponentUpdate():控制组件是否重新渲染
  • render():重新渲染
  • getSnapshotBeforeUpdate():获取更新前的快照
  • componentDidUpdate():组件更新完成

卸载阶段:

  • componentWillUnmount():组件即将卸载

详细解析: getDerivedStateFromProps是一个静态方法,不能访问组件实例,主要用于根据props的变化来更新state。getSnapshotBeforeUpdate在DOM更新之前调用,可以获取更新前的DOM信息。


Hooks

4. 什么是Hooks?常用的Hooks有哪些?

答案: Hooks是React 16.8引入的新特性,让函数组件也能使用state和其他React特性。

常用Hooks:

  • useState:管理组件状态
  • useEffect:处理副作用
  • useContext:使用Context
  • useReducer:复杂状态管理
  • useCallback:缓存函数
  • useMemo:缓存值
  • useRef:引用DOM元素或保存可变值

详细解析: Hooks遵循"只在最顶层调用"和"只在React函数中调用"的规则。useEffect可以替代componentDidMountcomponentDidUpdatecomponentWillUnmount,通过依赖数组控制执行时机。

代码示例:

import React, { useState, useEffect, useCallback, useMemo } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');

  // 副作用处理
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);

  // 缓存函数
  const handleClick = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  // 缓存计算值
  const expensiveValue = useMemo(() => {
    return count * 2;
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Expensive Value: {expensiveValue}</p>
      <button onClick={handleClick}>Increment</button>
      <input value={text} onChange={e => setText(e.target.value)} />
    </div>
  );
}

5. useEffect的依赖数组是什么?如何正确使用?

答案: useEffect的依赖数组决定了effect何时执行。

使用规则:

  • 空数组[]:只在组件挂载时执行一次
  • 包含依赖的数组:依赖变化时执行
  • 不传依赖数组:每次渲染后都执行

常见陷阱:

  • 忘记添加依赖导致闭包问题
  • 依赖数组包含对象或函数引用

正确使用示例:

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);

  // 正确:userId变化时重新获取用户信息
  useEffect(() => {
    fetchUser(userId).then(setUser);
  }, [userId]);

  // 错误:缺少依赖,可能导致闭包问题
  useEffect(() => {
    const timer = setInterval(() => {
      console.log('Current user:', user); // user可能是旧值
    }, 1000);
    return () => clearInterval(timer);
  }, []); // 应该包含user

  // 正确:使用useCallback避免函数引用变化
  const handleUserUpdate = useCallback((newData) => {
    setUser(prev => ({ ...prev, ...newData }));
  }, []);

  return <div>{user?.name}</div>;
}

性能优化

6. React性能优化的方法有哪些?

答案: React性能优化主要从以下几个方面入手:

1. 避免不必要的重新渲染

  • 使用React.memo包装函数组件
  • 使用PureComponentshouldComponentUpdate
  • 合理使用useCallbackuseMemo

2. 虚拟化长列表

  • 使用react-windowreact-virtualized

3. 代码分割

  • 使用React.lazySuspense

4. 避免内联对象和函数

详细解析: React.memo是一个高阶组件,它会对组件的props进行浅比较,如果props没有变化,组件就不会重新渲染。useCallbackuseMemo可以避免在每次渲染时创建新的函数和对象。

代码示例:

// 使用React.memo优化
const ExpensiveComponent = React.memo(({ data, onUpdate }) => {
  console.log('ExpensiveComponent rendered');
  return (
    <div>
      {data.map(item => (
        <div key={item.id}>{item.name}</div>
      ))}
      <button onClick={onUpdate}>Update</button>
    </div>
  );
});

// 父组件
function Parent() {
  const [count, setCount] = useState(0);
  const [data, setData] = useState([]);

  // 使用useCallback避免函数引用变化
  const handleUpdate = useCallback(() => {
    setData(prev => [...prev, { id: Date.now(), name: 'New Item' }]);
  }, []);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
      <ExpensiveComponent data={data} onUpdate={handleUpdate} />
    </div>
  );
}

状态管理

7. React中的状态管理有哪些方案?

答案: React状态管理方案从简单到复杂可以分为:

1. 本地状态

  • useState:简单状态
  • useReducer:复杂状态逻辑

2. 组件间状态共享

  • Props传递
  • Context API

3. 全局状态管理

  • Redux
  • Zustand
  • MobX
  • Recoil

详细解析: 对于小型应用,useStateuseReducer就足够了。当需要在多个组件间共享状态时,可以使用Context API。对于大型应用,建议使用专门的状态管理库。

Context API示例:

// 创建Context
const ThemeContext = React.createContext();

// 提供者组件
function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

// 消费者组件
function ThemedButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <button
      onClick={toggleTheme}
      style={{
        backgroundColor: theme === 'light' ? '#fff' : '#333',
        color: theme === 'light' ? '#333' : '#fff'
      }}
    >
      Toggle Theme
    </button>
  );
}

实战题目

8. 实现一个自定义Hook:useLocalStorage

题目要求: 创建一个自定义Hook,用于在localStorage中存储和获取数据,类似于useState的用法。

答案:

import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  // 获取localStorage中的值,如果没有则使用初始值
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(`Error reading localStorage key "${key}":`, error);
      return initialValue;
    }
  });

  // 设置localStorage的值
  const setValue = (value) => {
    try {
      // 允许值是一个函数,这样我们就可以像useState一样使用
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(`Error setting localStorage key "${key}":`, error);
    }
  };

  return [storedValue, setValue];
}

// 使用示例
function App() {
  const [name, setName] = useLocalStorage('name', 'Guest');
  const [count, setCount] = useLocalStorage('count', 0);

  return (
    <div>
      <h1>Hello, {name}!</h1>
      <input
        value={name}
        onChange={e => setName(e.target.value)}
        placeholder="Enter your name"
      />
      <p>Count: {count}</p>
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

解析: 这个自定义Hook的关键点:

  1. 使用useState的懒初始化函数来读取localStorage
  2. setValue中同时更新状态和localStorage
  3. 支持函数式更新,保持与useState一致的API
  4. 使用try-catch处理可能的错误

9. 实现一个防抖Hook:useDebounce

题目要求: 创建一个防抖Hook,用于延迟执行函数,避免频繁调用。

答案:

import { useState, useEffect } from 'react';

function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
}

// 使用示例
function SearchComponent() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  // 当debouncedSearchTerm变化时执行搜索
  useEffect(() => {
    if (debouncedSearchTerm) {
      performSearch(debouncedSearchTerm);
    }
  }, [debouncedSearchTerm]);

  const performSearch = (term) => {
    console.log('Searching for:', term);
    // 实际的搜索逻辑
  };

  return (
    <div>
      <input
        type="text"
        placeholder="Search..."
        value={searchTerm}
        onChange={e => setSearchTerm(e.target.value)}
      />
      <p>Searching for: {debouncedSearchTerm}</p>
    </div>
  );
}

解析: 这个Hook的工作原理:

  1. 接收一个值和延迟时间作为参数
  2. 使用useEffect监听值的变化
  3. 每次值变化时,清除之前的定时器并设置新的定时器
  4. 只有在延迟时间内没有新的值变化时,才会更新防抖值

总结

React面试题目涵盖了从基础概念到高级特性的各个方面。掌握这些知识点不仅有助于面试,更能提升日常开发能力。

关键要点:

  • 深入理解React的核心概念和设计理念
  • 熟练掌握Hooks的使用和最佳实践
  • 了解性能优化的方法和原理
  • 能够实现常见的自定义Hook
  • 掌握状态管理的不同方案

希望这篇文章能帮助到正在准备React面试的小伙伴们!如果觉得有用,别忘了点赞和收藏哦~


标签: #React #前端面试 #JavaScript #前端开发 #面试题