You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Reactjs+Redux+React Router应用:Redux更新致组件重复渲染咨询

Fixing Unnecessary Re-Renders & Duplicate API Calls in React + Redux + React Router

Hey there, let’s work through this issue you’re facing—this is a super common pain point when you’re ramping up with Redux, so we’ll break down exactly what’s happening and how to fix it.

Why All Components Are Re-Rendering

First, let’s get to the root of why every component is re-rendering when your FlashMessage state updates:

  • Overly Broad useSelector Calls: If you’re using useSelector(state => state) (grabbing the entire store) or selecting more state than your component actually needs, Redux will trigger a re-render every time any part of the store changes—even if your component doesn’t care about that change.
  • No Memoization for Selected State: By default, useSelector uses reference equality checks. If you return an object/array from useSelector without memoization, every store update will create a new reference, tricking React into thinking the state changed even when it didn’t.
  • Parent Component Re-Renders Causing Child Re-Renders: Even if a child component doesn’t use the updated state, if its parent re-renders, the child will too—unless you explicitly prevent it.

Why API Calls Are Happening Twice

The duplicate API calls are likely tied to the unnecessary re-renders, but here are the most common culprits:

  • Unoptimized useEffect Dependencies: If your API call is in a useEffect that has a dependency that changes on every re-render (like an unmemoized object), the effect will run again each time.
  • React Strict Mode: In development, React’s Strict Mode intentionally mounts components twice to detect accidental side effects. This can cause API calls to run twice—this is normal in dev and won’t happen in production.
  • Duplicate Action Dispatches: You might be dispatching the action that triggers the API call more than once (e.g., in multiple components or lifecycle methods).

Step-by-Step Fixes

1. Optimize Redux Selectors

  • Select Only What You Need: Instead of grabbing the entire store, pick exactly the state slice your component uses. For example:
    // Bad: Grabs entire store, causes re-renders on any state change
    const state = useSelector(state => state);
    
    // Good: Only selects the flashMessage slice
    const flashMessage = useSelector(state => state.flashMessage);
    
  • Use shallowEqual for Multiple Selections: If you need to pick multiple state slices, use shallowEqual to compare objects by value instead of reference:
    import { useSelector, shallowEqual } from 'react-redux';
    
    const MyComponent = () => {
      const { flashMessage, user } = useSelector(
        state => ({
          flashMessage: state.flashMessage,
          user: state.user
        }),
        shallowEqual // Compares object values instead of references
      );
      // ...
    };
    
  • Create Memoized Selectors with Reselect: For more complex state selections (like derived data), use Reselect to create memoized selectors that only recalculate when their inputs change:
    import { createSelector } from '@reduxjs/toolkit';
    
    // Base selectors
    const selectFlashMessage = state => state.flashMessage;
    const selectUser = state => state.user;
    
    // Memoized combined selector
    export const selectRelevantData = createSelector(
      [selectFlashMessage, selectUser],
      (flashMessage, user) => ({ flashMessage, user })
    );
    
    // In your component:
    const data = useSelector(selectRelevantData);
    

2. Prevent Unnecessary Component Re-Renders

  • Wrap Components with React.memo: This tells React to only re-render the component if its props have changed (shallow comparison):
    import React from 'react';
    
    const FlashMessage = React.memo(({ message }) => {
      return <div className="flash-message">{message}</div>;
    });
    
    export default FlashMessage;
    
  • Memoize Props with useMemo/useCallback: If you’re passing objects, arrays, or functions as props to child components, use useMemo (for values) or useCallback (for functions) to keep their references stable:
    const ParentComponent = () => {
      const flashMessage = useSelector(state => state.flashMessage);
      // Memoize the object to prevent unnecessary child re-renders
      const messageProps = useMemo(() => ({ text: flashMessage.text }), [flashMessage.text]);
    
      return <FlashMessage {...messageProps} />;
    };
    

3. Fix Duplicate API Calls

  • Audit useEffect Dependencies: Make sure your API call’s useEffect only runs when necessary. For example, if it should run once on mount, use an empty dependency array:
    useEffect(() => {
      const fetchData = async () => {
        const response = await fetch('/api/data');
        const data = await response.json();
        dispatch(setData(data));
      };
      fetchData();
    }, []); // Empty array = runs once on mount
    
  • Check for Strict Mode: If you’re seeing duplicate calls in development only, it’s likely React Strict Mode. You can temporarily remove <React.StrictMode> from your root component to verify, but remember this is a dev-only check—your production build won’t have this behavior.
  • Verify Action Dispatches: Double-check that you’re not dispatching the API-triggering action in multiple places (e.g., in both a parent and child component, or in a lifecycle method that runs on every re-render).

Final Notes

Start with optimizing your selectors—this is usually the biggest win for reducing unnecessary re-renders. Then layer in React.memo and memoized props as needed. For the API calls, narrow down whether it’s a dependency issue, Strict Mode, or duplicate dispatches.

内容的提问来源于stack exchange,提问作者chifliiiii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:16:11