admin管理员组

文章数量:1193758

const initialState = {
  productEditor: {
    productDetails: {
      id: 1,
      name: "Product A",
      description: "A great product",
    },
    tags: ["tag1", "tag2"],
    images: {
      main: "image1.png",
      gallery: ["image2.png", "image3.png"],
    },
  },
};

Using React-Redux selectors suppose one component subscribes to productDetails.name and another subscribes to images.main but I change productDetails.name, will this cause other components to re-render? Is it possible to divide a state into categories and only subscribe to any depth property avoiding unnecessary re-renders? As I understand, Redux compares references so if I just reassign an existing object its ref wont change so that should not cause re-render.

const initialState = {
  productEditor: {
    productDetails: {
      id: 1,
      name: "Product A",
      description: "A great product",
    },
    tags: ["tag1", "tag2"],
    images: {
      main: "image1.png",
      gallery: ["image2.png", "image3.png"],
    },
  },
};

Using React-Redux selectors suppose one component subscribes to productDetails.name and another subscribes to images.main but I change productDetails.name, will this cause other components to re-render? Is it possible to divide a state into categories and only subscribe to any depth property avoiding unnecessary re-renders? As I understand, Redux compares references so if I just reassign an existing object its ref wont change so that should not cause re-render.

Share Improve this question edited Jan 24 at 17:52 Drew Reese 203k17 gold badges234 silver badges266 bronze badges asked Jan 24 at 12:54 amtamt 154 bronze badges 0
Add a comment  | 

1 Answer 1

Reset to default 0

Using React-Redux selectors suppose one component subscribes to productDetails.name and another subscribes to images.main but I change productDetails.name will this cause other components to re-render?

No, only when what the useSelector hook is selecting changes does this trigger the subscribed component to rerender. In other words, components only rerender when what they are subscribed to changes. If one component is subscribed to productDetails.name and another is subscribed to images.main, then state updates specifically to productDetails.name does not trigger the component subscribed to images.main to rerender, and vice-versa.

Demo

Consider this demo sandbox:

Demo Code:

index.js

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Provider } from "react-redux";

import App from "./App";
import { store } from "./store";

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);

root.render(
  <StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </StrictMode>
);

store.js

import { configureStore } from "@reduxjs/toolkit";
import stateReducer from "./state.slice";

export const store = configureStore({
  reducer: stateReducer,
});

state.slice.js

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
  productEditor: {
    productDetails: {
      id: 1,
      name: "Product A",
      description: "A great product",
    },
    tags: ["tag1", "tag2"],
    images: {
      main: "image1.png",
      gallery: ["image2.png", "image3.png"],
    },
  },
};

const stateSlice = createSlice({
  name: "state",
  initialState,
  reducers: {
    updateProductName: (state, action) => {
      state.productEditor.productDetails.name = action.payload;
    },
    updateImagesMain: (state, action) => {
      state.productEditor.images.main = action.payload;
    },
  },
});

export const { updateImagesMain, updateProductName } = stateSlice.actions;

export default stateSlice.reducer;

App.js

import { useEffect, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import { nanoid } from "@reduxjs/toolkit";
import { updateImagesMain, updateProductName } from "./state.slice";

const ComponentA = () => {
  const dispatch = useDispatch();
  const productDetails = useSelector(
    (state) => state.productEditor.productDetails
  );
  const renderCount = useRef(0);

  useEffect(() => {
    console.log("ComponentA RENDERED");
    renderCount.current++;
  });

  return (
    <div>
      <h1>Product Details</h1>
      <button
        type="button"
        onClick={() => dispatch(updateProductName(nanoid()))}
      >
        Update Product Name
      </button>
      <p>{JSON.stringify(productDetails)}</p>
      <p>Render Count: {renderCount.current}</p>
    </div>
  );
};

const ComponentB = () => {
  const dispatch = useDispatch();
  const images = useSelector((state) => state.productEditor.images);
  const renderCount = useRef(0);

  useEffect(() => {
    console.log("ComponentB RENDERED");
    renderCount.current++;
  });

  return (
    <div>
      <h1>Images</h1>
      <button
        type="button"
        onClick={() => dispatch(updateImagesMain(nanoid()))}
      >
        Update Images Main
      </button>
      <p>{JSON.stringify(images)}</p>
      <p>Render Count: {renderCount.current}</p>
    </div>
  );
};

export default function App() {
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>

      <ComponentA />
      <ComponentB />
    </div>
  );
}

Note

Because of the way React-Redux is optimized via the useSelector hook you'll want to be as granular and specific as to the state values you are selecting. Selecting more than you actually need may trigger unnecessary component re-renders. For example, if both components subscribed to state.productEditor instead of the more deeply nested states, then any time state.productEditor.productDetails.* or state.productEditor.images.* updated, both subscribers would re-render.

const { productDetails } = useSelector(state => state.productEditor);
const { images } = useSelector(state => state.productEditor);

Updates to anything in state.productEditor will cause both these susbcribers to re-render.

本文标签: