skip to Main Content

I’m trying to create a tabbing system. Here is the simplified version of the code:

import { useState, useEffect } from "react";

function Test({ id }) {
  const [text, setText] = useState("");

  return (
    <div>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Type something..."
      />
      <h1>{text}</h1>
    </div>
  );
}

function App() {
  const [active, setActive] = useState(0);

  const [components, setCompoents] = useState([
    <Test key="1" id="1" />,
    <Test key="2" id="2" />,
    <Test key="3" id="3" />,
  ]);

  const handleActiiveChange = () => {
    setActive((active + 1) % components.length);
  };

  return (
    <div>
      <button onClick={handleActiiveChange}>Switch Tab</button>
      change: {active.toString()}
      <div>{components[active]}</div>
    </div>
  );
}

export default App;

In the final version, new tabs will be added dynamically to components. Although the current code sort of "works", the components (Test) do not preserve state as I would have hoped. The goal is that whenever a tab is rendered it keeps its previous state (e.g.: if I typed something in it, and then I cycle back to the tab I typed in it should not be lost.

What are common and best practices to "freeze", or store a react component in memory?

Ideally, the Test component (or whatever components will be put into this system) should not know anything about if they are active, or that they are in a tabbing system. They should just be regular components.

Thank you for reading!

2

Answers


  1. After mounting the Test component, the default value is set in the useState hook. In order for it to "remember" the value after unmounting, you need to take this state higher, you can put it inside the parent component – App. After that, using the props, you will be able to throw the necessary data into the components you need.

    For more convenient work with data, you can use various state managers – for example, redux or mox.

    Login or Signup to reply.
  2. Storing or "freezing" the state of a React component as you have described while also maintaining each component’s state within the component itself, rather than lifting it up, can be achieved by rendering all components simultaneously but conditionally showing/hiding them based on the currently active tab. That can be accomplished using CSS.

    I see that, when a React component is unmounted, its state is lost and when it is remounted again, it starts with a fresh state. So, to preserve the state of the components, you would need to… avoid unmounting them.

    A possible solution would be to render all the components, but only displaying the active one.
    But… that means also keeping them all in memory, which can limit the scalability of this solution.

    import { useState, useEffect } from "react";
    
    function Test({ id }) {
      const [text, setText] = useState("");
    
      return (
        <div>
          <input
            type="text"
            value={text}
            onChange={(e) => setText(e.target.value)}
            placeholder="Type something"
          />
          <h1>{text}</h1>
        </div>
      );
    }
    
    function App() {
      const [active, setActive] = useState(0);
    
      const components = [<Test key="1" id="1" />, <Test key="2" id="2" />, <Test key="3" id="3" />];
    
      const handleActiveChange = () => {
        setActive((active + 1) % components.length);
      };
    
      return (
        <div>
          <button onClick={handleActiveChange}>Switch Tab</button>
          change: {active.toString()}
          <div>
            {components.map((component, index) => (
              <div style={{ display: active === index ? 'block' : 'none' }}>
                {component}
              </div>
            ))}
          </div>
        </div>
      );
    }
    
    export default App;
    

    That means all the Test components are rendered at the same time. Each Test component is wrapped in a div element, and the display style of this wrapper div is conditionally set based on the active index. Only the active component is displayed, the rest are hidden. Since all the components are rendered, their state should be preserved between tab switches.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search