React nested map sequential render with setTimeout Code Answer

Hello Developer, Hope you guys are doing great. Today at Tutorial Guruji Official website, we are sharing the answer of React nested map sequential render with setTimeout without wasting too much if your time.

The question is published on by Tutorial Guruji team.

My goal is to loop through an array of characters and end on each letter of a given word. My code is currently displaying all of these elements at once, but I want them to display sequentially. Here’s what I currently have:

Current view

I’d like to return the array that ends with h(wait a few moments), array that ends with e (wait a few moments), and so on. I can’t figure out to to attach the arrayIndex to the nested map though.

DisplayName.js

import React, { useState, useEffect } from "react";

const DisplayName = ({ characters, first }) => {
  const [charIndex, setCharIndex] = useState(0);
  const [arrayIndex, setArrayIndex] = useState(0);
  
  let arrayContainer = [];

  first.map((letter, i) => {
    arrayContainer.push([]);
    arrayContainer[i].push(characters.concat(first[i]));
    return arrayContainer;
  });

// I can't figure out how to attach arrayIndex here. I am
// also not using j currently, but kept it for now in case I need
// a key for the return statements.
  const fullList = arrayContainer.map((letterArr, j) => {
    return letterArr.map(char => {
        return (char[charIndex])
      })
  });

  useEffect(() => {
    let timer;
    let secondTimer;

    if (charIndex < characters.length) {
      timer = setTimeout(() => {
        setCharIndex(charIndex + 1)
      }, 75)
    }

    if (arrayIndex < first.length - 1) {
      secondTimer = setTimeout(() => {
        setArrayIndex(arrayIndex + 1)
      }, 75)
    }

    return () => {
      clearTimeout(timer);
      clearTimeout(secondTimer);
    };
  }, [charIndex, characters, arrayIndex, first]);

  return (
    <div>{fullList}</div>
  )
};

export default DisplayName;

App.js

import React from 'react';
import DisplayName from './DisplayName';
import './App.css';

function App() {
    const first = 'hello'.split('');
    const funChars = [
      '⏀', '⎷', '⌮', '⋙', '⊠', '⎳', '⍼',
     '⍣', '╈', '╳', '☀', '★', '☍',  'ↂ','▅'];

  return (
    <div className="glow" style={{ minHeight: '100vh'}}>
      <span style={{ letterSpacing: 12}}><DisplayName first={first} characters={funChars}/></span>
    </div>
  );
}

export default App;

I’ve also tried something like const [rendered, setRendered] = useState(false); without success, which I tried attaching to the j key.

Answer

If I understand your question, you want to iterate over the first string up to an index and display a “rolling” fun character while iterating the string.

Intuitively I think it is easier to think of of slicing the front of the first string to an index, and appending the fun character.

iteration index text.substring(0, index) result(s)
0 0 “” ‘⏀’, ‘⎷’, ‘⌮’,…
1 1 “h” ‘h⏀’, ‘h⎷’, ‘h⌮’,…
2 2 “he” ‘he⏀’, ‘he⎷’, ‘he⌮’,…
3 3 “hel” ‘hel⏀’, ‘hel⎷’, ‘hel⌮’,…
4 4 “hell” ‘hell⏀’, ‘hell⎷’, ‘hell⌮’,…
5 5 “hello” ‘hello’

The tricky issue is using two separate timers/intervals to increment the index for the first string and to increment an index into the fun characters array. Here is a solution I came up with.

  1. Use a React ref to hold a interval timer reference for the rolling fun characters.
  2. Single useEffect hook to start the “rolling” fun character index incrementing on an interval. Start a timeout on incrementing over the first string char array, if there is still length to iterate, enqueue another timeout, otherwise run clean up functions to clear timers and state.
  3. Slice the first string up to index arrayIndex and conditionally append a “rolling” fun character.

Code:

const DisplayName = ({ characters, first }) => {
  const charTimerRef = useRef(null);
  const [charIndex, setCharIndex] = useState(null);
  const [arrayIndex, setArrayIndex] = useState(0);

  useEffect(() => {
    let timerId;
    const cleanupTimerRef = () => {
      setCharIndex(null);
      clearInterval(charTimerRef.current);
      charTimerRef.current = null;
    };

    if (!charTimerRef.current) {
      setCharIndex(0);
      charTimerRef.current = setInterval(() => {
        setCharIndex((i) => i + 1);
      }, 75);
    }

    if (arrayIndex < first.length) {
      timerId = setTimeout(() => {
        setArrayIndex((i) => i + 1);
      }, 1000);
    } else {
      cleanupTimerRef();
    }

    return () => {
      clearTimeout(timerId);
      cleanupTimerRef();
    };
  }, [arrayIndex, first]);

  const fullList =
    first.substring(0, arrayIndex) +
    (charIndex ? characters[charIndex % characters.length] : "");

  return <div>{fullList}</div>;
};

Demo

Edit react-nested-map-sequential-render-with-settimeout

We are here to answer your question about React nested map sequential render with setTimeout - If you find the proper solution, please don't forgot to share this with your team members.

Related Posts

Tutorial Guruji