Skip to content

React 基础面试题

1. React 基础概念

问题:React 的核心特性是什么?

答案

核心特性

  1. 组件化
jsx
// 函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

// 使用
<Welcome name="John" />
  1. JSX
jsx
// JSX 语法
const element = <h1>Hello, world!</h1>;

// 编译为
const element = React.createElement(
  'h1',
  null,
  'Hello, world!'
);

// 表达式
const name = 'John';
const element = <h1>Hello, {name}</h1>;

// 属性
const element = <div className="greeting">Hello</div>;

// 子元素
const element = (
  <div>
    <h1>Hello</h1>
    <p>Good to see you</p>
  </div>
);
  1. 虚拟 DOM
  • React 使用虚拟 DOM 来提高渲染性能
  • 通过 diff 算法比较新旧虚拟 DOM
  • 只更新需要变化的部分
  1. 单向数据流
jsx
// 父组件传递数据给子组件
function Parent() {
  const [count, setCount] = useState(0);
  return <Child count={count} />;
}

function Child({ count }) {
  return <p>Count: {count}</p>;
}

2. State 和 Props

问题:React 中的 State 和 Props 有什么区别?

答案

Props

jsx
// Props 是从父组件传递给子组件的数据
function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}

// 使用
<Greeting name="John" />

// 解构 props
function Greeting({ name, age }) {
  return (
    <div>
      <h1>Hello, {name}!</h1>
      <p>Age: {age}</p>
    </div>
  );
}

// Props 类型检查
import PropTypes from 'prop-types';

Greeting.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number
};

// 默认 props
Greeting.defaultProps = {
  name: 'Guest',
  age: 0
};

State

jsx
import { useState } from 'react';

function Counter() {
  // useState 返回 [当前值, 更新函数]
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

// 类组件中的 state
class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
  
  increment = () => {
    this.setState({ count: this.state.count + 1 });
  }
  
  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

State 和 Props 的区别

特性PropsState
来源父组件传递组件内部
可变性只读可变
作用域组件间传递组件内部
更新方式父组件更新setState/setState

3. 生命周期

问题:React 组件的生命周期有哪些?

答案

函数组件生命周期(Hooks)

jsx
import { useState, useEffect, useRef } from 'react';

function LifecycleComponent() {
  const [count, setCount] = useState(0);
  const mounted = useRef(false);
  
  // componentDidMount + componentDidUpdate
  useEffect(() => {
    console.log('组件已挂载或更新');
  });
  
  // componentDidMount
  useEffect(() => {
    console.log('组件已挂载');
    mounted.current = true;
    
    // componentWillUnmount
    return () => {
      console.log('组件将卸载');
      mounted.current = false;
    };
  }, []);
  
  // 监听特定依赖
  useEffect(() => {
    console.log('count 变化了:', count);
  }, [count]);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
    </div>
  );
}

类组件生命周期

jsx
class LifecycleComponent extends React.Component {
  // 挂载阶段
  constructor(props) {
    super(props);
    console.log('constructor');
  }
  
  static getDerivedStateFromProps(props, state) {
    console.log('getDerivedStateFromProps');
    return null;
  }
  
  componentDidMount() {
    console.log('componentDidMount');
  }
  
  // 更新阶段
  shouldComponentUpdate(nextProps, nextState) {
    console.log('shouldComponentUpdate');
    return true;
  }
  
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('getSnapshotBeforeUpdate');
    return null;
  }
  
  componentDidUpdate(prevProps, prevState) {
    console.log('componentDidUpdate');
  }
  
  // 卸载阶段
  componentWillUnmount() {
    console.log('componentWillUnmount');
  }
  
  render() {
    return <div>Component</div>;
  }
}

4. React Hooks

问题:React Hooks 如何使用?

答案

useState

jsx
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setCount(prev => prev + 1)}>
        Increment with callback
      </button>
    </div>
  );
}

// 对象 state
function UserForm() {
  const [user, setUser] = useState({
    name: '',
    email: ''
  });
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    setUser(prev => ({
      ...prev,
      [name]: value
    }));
  };
  
  return (
    <form>
      <input
        name="name"
        value={user.name}
        onChange={handleChange}
      />
      <input
        name="email"
        value={user.email}
        onChange={handleChange}
      />
    </form>
  );
}

useEffect

jsx
function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('/api/data');
        const result = await response.json();
        setData(result);
      } catch (error) {
        console.error('Error:', error);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, []); // 空依赖数组,只在挂载时执行
  
  useEffect(() => {
    document.title = `Data: ${data?.title}`;
  }, [data]); // data 变化时执行
  
  if (loading) return <p>Loading...</p>;
  
  return <div>{data?.title}</div>;
}

useContext

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

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return <ThemedButton />;
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>Click me</button>;
}

useReducer

jsx
const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>
        Increment
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        Decrement
      </button>
    </div>
  );
}

useMemo 和 useCallback

jsx
function ExpensiveComponent({ a, b }) {
  // 缓存计算结果
  const expensiveValue = useMemo(() => {
    console.log('Computing expensive value');
    return a * b;
  }, [a, b]);
  
  // 缓存函数
  const handleClick = useCallback(() => {
    console.log('Clicked');
  }, []);
  
  return (
    <div>
      <p>Value: {expensiveValue}</p>
      <button onClick={handleClick}>Click</button>
    </div>
  );
}

useRef

jsx
function FocusInput() {
  const inputRef = useRef(null);
  
  const handleClick = () => {
    inputRef.current.focus();
  };
  
  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Focus Input</button>
    </div>
  );
}

// 保存之前的值
function PreviousValue({ value }) {
  const prevValue = useRef();
  
  useEffect(() => {
    prevValue.current = value;
  }, [value]);
  
  return (
    <div>
      <p>Current: {value}</p>
      <p>Previous: {prevValue.current}</p>
    </div>
  );
}

5. 事件处理

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

答案

基本事件处理

jsx
function Button() {
  const handleClick = (e) => {
    e.preventDefault();
    console.log('Button clicked');
  };
  
  return (
    <button onClick={handleClick}>
      Click me
    </button>
  );
}

// 传递参数
function ButtonList() {
  const buttons = ['A', 'B', 'C'];
  
  return (
    <div>
      {buttons.map((button, index) => (
        <button
          key={index}
          onClick={() => console.log(button)}
        >
          {button}
        </button>
      ))}
    </div>
  );
}

事件对象

jsx
function Form() {
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form submitted');
  };
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    console.log(`${name}: ${value}`);
  };
  
  const handleKeyDown = (e) => {
    if (e.key === 'Enter') {
      console.log('Enter key pressed');
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="username"
        onChange={handleChange}
        onKeyDown={handleKeyDown}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

合成事件

jsx
function EventDemo() {
  const handleClick = (e) => {
    // 合成事件对象
    console.log('Event type:', e.type);
    console.log('Event target:', e.target);
    console.log('Event currentTarget:', e.currentTarget);
    
    // 原生事件
    console.log('Native event:', e.nativeEvent);
  };
  
  return <button onClick={handleClick}>Click me</button>;
}

6. 条件渲染和列表渲染

问题:React 中如何进行条件渲染和列表渲染?

答案

条件渲染

jsx
function Greeting({ isLoggedIn }) {
  // 三元运算符
  return (
    <div>
      {isLoggedIn ? <Welcome /> : <Login />}
    </div>
  );
}

// 逻辑与运算符
function Notification({ message }) {
  return (
    <div>
      {message && <p>{message}</p>}
    </div>
  );
}

// 立即调用函数
function Greeting({ user }) {
  return (
    <div>
      {(() => {
        if (!user) return <Login />;
        if (user.role === 'admin') return <AdminPanel />;
        return <UserPanel />;
      })()}
    </div>
  );
}

列表渲染

jsx
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={todo.id}>
          {index + 1}. {todo.text}
        </li>
      ))}
    </ul>
  );
}

// 嵌套列表
function NestedList({ data }) {
  return (
    <ul>
      {data.map(item => (
        <li key={item.id}>
          {item.name}
          {item.children && (
            <NestedList data={item.children} />
          )}
        </li>
      ))}
    </ul>
  );
}

7. 表单处理

问题:React 中如何处理表单?

答案

受控组件

jsx
function Form() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: ''
  });
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('Form data:', formData);
  };
  
  return (
    <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>
      <div>
        <label>Password:</label>
        <input
          type="password"
          name="password"
          value={formData.password}
          onChange={handleChange}
        />
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

非受控组件

jsx
function UncontrolledForm() {
  const usernameRef = useRef();
  const emailRef = useRef();
  
  const handleSubmit = (e) => {
    e.preventDefault();
    const formData = {
      username: usernameRef.current.value,
      email: emailRef.current.value
    };
    console.log('Form data:', formData);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Username:</label>
        <input
          type="text"
          name="username"
          ref={usernameRef}
          defaultValue=""
        />
      </div>
      <div>
        <label>Email:</label>
        <input
          type="email"
          name="email"
          ref={emailRef}
          defaultValue=""
        />
      </div>
      <button type="submit">Submit</button>
    </form>
  );
}

表单验证

jsx
function ValidatedForm() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: ''
  });
  
  const [errors, setErrors] = useState({});
  
  const validate = () => {
    const newErrors = {};
    
    if (!formData.username) {
      newErrors.username = 'Username is required';
    }
    
    if (!formData.email) {
      newErrors.email = 'Email is required';
    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
      newErrors.email = 'Email is invalid';
    }
    
    if (!formData.password) {
      newErrors.password = 'Password is required';
    } else if (formData.password.length < 6) {
      newErrors.password = 'Password must be at least 6 characters';
    }
    
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    if (validate()) {
      console.log('Form submitted:', formData);
    }
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>Username:</label>
        <input
          type="text"
          name="username"
          value={formData.username}
          onChange={(e) => setFormData({...formData, username: e.target.value})}
        />
        {errors.username && <span className="error">{errors.username}</span>}
      </div>
      {/* 其他字段... */}
      <button type="submit">Submit</button>
    </form>
  );
}

8. 组件通信

问题:React 组件之间如何通信?

答案

父子通信

jsx
// 父组件
function Parent() {
  const [message, setMessage] = useState('Hello from parent');
  
  const handleMessage = (msg) => {
    setMessage(msg);
  };
  
  return (
    <div>
      <p>Message: {message}</p>
      <Child message={message} onMessage={handleMessage} />
    </div>
  );
}

// 子组件
function Child({ message, onMessage }) {
  const handleClick = () => {
    onMessage('Hello from child');
  };
  
  return (
    <div>
      <p>Received: {message}</p>
      <button onClick={handleClick}>Send Message</button>
    </div>
  );
}

兄弟组件通信

jsx
// 使用 Context
const MessageContext = React.createContext();

function Parent() {
  const [message, setMessage] = useState('');
  
  return (
    <MessageContext.Provider value={{ message, setMessage }}>
      <SiblingA />
      <SiblingB />
    </MessageContext.Provider>
  );
}

function SiblingA() {
  const { setMessage } = useContext(MessageContext);
  
  return (
    <button onClick={() => setMessage('Hello from A')}>
      Send Message
    </button>
  );
}

function SiblingB() {
  const { message } = useContext(MessageContext);
  
  return <p>Message: {message}</p>;
}

跨层级通信

jsx
// 使用 Context 跨层级传递数据
const ThemeContext = React.createContext();

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Header />
      <Main />
      <Footer />
    </ThemeContext.Provider>
  );
}

function Header() {
  const theme = useContext(ThemeContext);
  return <header className={theme}>Header</header>;
}

function Main() {
  const theme = useContext(ThemeContext);
  return <main className={theme}>Main</main>;
}

9. React Router

问题:React Router 如何使用?

答案

基本路由

jsx
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
        <Link to="/users">Users</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users" element={<Users />} />
      </Routes>
    </BrowserRouter>
  );
}

动态路由

jsx
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/users/:id" element={<UserDetail />} />
      </Routes>
    </BrowserRouter>
  );
}

function UserDetail() {
  const { id } = useParams();
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch(`/api/users/${id}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [id]);
  
  if (!user) return <p>Loading...</p>;
  
  return <div>User: {user.name}</div>;
}

嵌套路由

jsx
function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/admin" element={<AdminLayout />}>
          <Route path="dashboard" element={<Dashboard />} />
          <Route path="users" element={<UserManagement />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </BrowserRouter>
  );
}

function AdminLayout() {
  return (
    <div>
      <nav>
        <Link to="/admin/dashboard">Dashboard</Link>
        <Link to="/admin/users">Users</Link>
        <Link to="/admin/settings">Settings</Link>
      </nav>
      <Outlet />
    </div>
  );
}

路由守卫

jsx
function ProtectedRoute({ children }) {
  const isAuthenticated = useAuth();
  
  if (!isAuthenticated) {
    return <Navigate to="/login" />;
  }
  
  return children;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<Login />} />
        <Route
          path="/dashboard"
          element={
            <ProtectedRoute>
              <Dashboard />
            </ProtectedRoute>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}

10. React 性能优化

问题:如何优化 React 组件性能?

答案

React.memo

jsx
const ExpensiveComponent = React.memo(function ExpensiveComponent({ data }) {
  console.log('ExpensiveComponent rendered');
  return <div>{data}</div>;
});

// 使用
function Parent() {
  const [count, setCount] = useState(0);
  const data = useMemo(() => 'expensive data', []);
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <ExpensiveComponent data={data} />
    </div>
  );
}

useMemo 和 useCallback

jsx
function OptimizedComponent({ a, b, onClick }) {
  // 缓存计算结果
  const result = useMemo(() => {
    console.log('Computing...');
    return a * b;
  }, [a, b]);
  
  // 缓存函数
  const handleClick = useCallback(() => {
    console.log('Clicked');
    onClick();
  }, [onClick]);
  
  return (
    <div>
      <p>Result: {result}</p>
      <button onClick={handleClick}>Click</button>
    </div>
  );
}

虚拟列表

jsx
import { FixedSizeList } from 'react-window';

function VirtualList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>Item {items[index]}</div>
  );
  
  return (
    <FixedSizeList
      height={400}
      itemCount={items.length}
      itemSize={35}
      width={300}
    >
      {Row}
    </FixedSizeList>
  );
}

代码分割

jsx
import { lazy, Suspense } from 'react';

// 懒加载组件
const LazyComponent = lazy(() => import('./LazyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

避免不必要的渲染

jsx
// 使用 shouldComponentUpdate(类组件)
class OptimizedComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    return nextProps.id !== this.props.id;
  }
  
  render() {
    return <div>{this.props.data}</div>;
  }
}

// 使用 React.memo(函数组件)
const MemoizedComponent = React.memo(function MemoizedComponent({ data }) {
  return <div>{data}</div>;
}, (prevProps, nextProps) => {
  return prevProps.data === nextProps.data;
});