skip to Main Content

The example that I’m dealing with: I have an array that I’m mapping over. For each index of the array, one component is to be shown, and then after x time it’s replaced with a different component (permanently).

const Parent = ({ array }) => {
  const [isVisible, setIsVisible] = useState(true);

  setTimeout(() => {
    setIsVisible(false);
  }, 1000);

  return array.map((item, index) => {
    return (
      <div key={index}>
        {isVisible && <Child>{item}</Child>}
        {!isVisible && <Child2>{item}</Child2>}
      </div>
    )
  })
}

The above code works as intended at the first index. After the state change, following array items do not ‘switch’ components. Only the second component is rendered (because !isVisible is true).

Is there a way to have a state variable for each index of this dynamic array?

2

Answers


  1. You should perform your setTimeout inside a useEffect, otherwise it’ll run every render.

    To answer your question though, yes you could put this in its own component to simplify the logistics of it. This lets each element manage its own timer instead of the parent managing all its children. This only works for simple cases. If you wanted something more complex, for example, each child is visible one second after the last one, the parent would need to do some management via forwardProps or props or broadcast events.

    function ChildWrapper(props) {
        const { item } = props;
        const [isVisible, setIsVisible] = useState(true);
    
        useEffect(() => {
            let isMounted = true;
    
            setTimeout(() => {
                // Do not call state changes on unmounted components
                if (!isMounted) return;
                setIsVisible(false);
            }, 1000);
    
            return () => {
                isMounted = false;
            }
        }, []);
    
        return (
            {isVisible
                ? <Child>{item}</Child>
                : <Child2>{item}</Child2>
            }
        );
    }
    
    const Parent = ({ array }) => {
      return array.map((item: any, index: number) => {
        return (
          <div key={index}>
            <ChildWrapper item={item} />
          </div>
        )
      })
    }
    
    Login or Signup to reply.
  2. You can extract the logic into a separate component.

    function ChildWrapper({ item }) {
        const [showFirst, setShowFirst] = useState(true);
        useEffect(() => {
            setTimeout(() => setShowFirst(false), 1000);
        }, []);
        return <div>
            {showFirst ? <Child>{item}</Child> : <Child2>{item}</Child2>}
        </div>;
    }
    
    const Parent = ({ array }) => {
        return array.map((item, index) => <ChildWrapper key={index} item={item}/>);
    };
    

    Working example:

    function Child({ children }) {
      return 'child 1: ' + children;
    }
    function Child2({ children }) {
      return 'child 2: ' + children;
    }
    function ChildWrapper({ item }) {
        const [showFirst, setShowFirst] = React.useState(true);
        React.useEffect(() => {
            setTimeout(() => setShowFirst(false), 1000);
        }, []);
        return <div>
            {showFirst ? <Child>{item}</Child> : <Child2>{item}</Child2>}
        </div>;
    }
    const Parent = ({ array }) => {
        return array.map((item, index) => <ChildWrapper key={index} item={item}/>);
    };
    function App() {
      const [arr, setArr] = React.useState(['a', 'b', 'c']);
      return (
        <React.Fragment>
          <Parent array={arr}/>
          <button onClick={() => setArr(arr.concat(Math.random()))}>
            Add
          </button>
        </React.Fragment>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
    <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
    <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
    <div id="root"></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search