Home > OS >  Print component render number in React with Redux
Print component render number in React with Redux

Time:04-15

I have the following problem. I want to print a number that is equivalent to the order in which it is provided. For this, I have created a component called a title that prints the value of a data in Redux. Also in this same component, I use the security hook, which is executed for each representation of the component. In the theory, if I have the title component 6 times in my application, 6 times will render and 6 times it will be executed. Will be executed. Following this logic, in the installation, I use the shipment to set the value as I print on the component. The reducing function is simply an increase function, in theory, the correct behavior must be that the application prints the order number in which the component is rendered. But he does not do that, what he does is print a fixed number on all the components. Here the code code: https://codesandbox.io/s/redux-call-api-eke2?file=/src/app.js

here the code:

App Component

   import React from "react";
import "./App.css";
import Title from "./Title";

function App() {
  return (
    <div className="App">
      <div>
        <Title />
      </div>
      <div>
        <Title />
        <div>
          <Title />
        </div>
      </div>
      <div>
        <Title />
      </div>
      <Title />
      <Title />
      <Title />
      <Title />
      <Title />
    </div>
  );
}

export default App;

Slice Redux

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

export const slice = createSlice({
  name: "counter",
  initialState: {
    value: 0
  },
  reducers: {
    increment: (state) => {
      // Redux Toolkit allows us to write "mutating" logic in reducers. Item
      // doesn't actually mutate the state because it uses the immer library,
      // which detects changes to a "draft state" and produces a brand new
      // immutable state based off those changes
      state.value  = 1;
    }
  }
});

export const { increment } = slice.actions;

export default slice.reducer;

Title component

import React, { useEffect } from "react";
import { useSelector, useDispatch } from "react-redux";
import {increment} from "./features/counter/counterSlice"

functionTitle() {
  const Counter = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  useEffect(() => {
    console.log("hi");
    dispatch(increment())
  }, []);

  return <h2>Render {Counter}</h2>;
}

export default Title;

CodePudding user response:

While I still don't fully understand the real-world use-case. Here is a way you could accomplish this.

You do not want to use redux for this. The whole point of state (redux or otherwise) is to hold values that should change over time, and re-render any components that use them when the state updates. So by using Redux to hold the counter, you are forcing each Title component to re-render until they all use the latest value (6).

Instead, you could use a regular variable, store it in a ref, and then increment it each time it's used.

This variable could easily be pulled out into it's own file to be imported in other places.

The useRef is needed so that we only read from global_counter once on the first render.

let global_counter = 1;

function Title() {
  const local_counter = React.useRef(global_counter  );

  return <h2>Render {local_counter.current}</h2>;
}

function App() {
  return (
    <div>
      <div>
        <Title />
      </div>
      <div>
        <Title />
        <div>
          <Title />
        </div>
      </div>
      <div>
        <Title />
      </div>
      <Title />
      <Title />
      <Title />
      <Title />
      <Title />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

I suppose you could even make this a custom hook, and expand the logic a bit to handle re-renders incrementing the global counter; making this the preferred solution:

let global_counter = 1;

function useGlobalCounter() {
  const [count, setCount] = React.useState(() => global_counter  );
  
  return count;
}

function Title() {
  const local_counter = useGlobalCounter();
  const [state, setState] = React.useState(false);

  console.log(global_counter);
  return (
    <div>
      <h2>Render {local_counter}</h2>
      <button onClick={() => setState(!state)}>Re-render me</button>
    </div>
  );
}

function App() {
  return (
    <div>
      <Title />
      <Title />
      <Title />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

This solution is more robust since it handles re-renders in a way that does not increment global_counter. It does this by using the function initializer of useState so that global_counter is not evaluated each render - only the first.

  • Related