使用 setTimeout 反应嵌套地图顺序渲染

React nested map sequential render with setTimeout

我的目标是遍历字符数组并在给定单词的每个字母处结束。我的代码当前同时显示所有这些元素,但我希望它们按顺序显示。这是我目前拥有的:

Current view

我要return以h结尾的数组(稍等片刻),e[=结尾的数组31=](稍等片刻),依此类推。我不知道如何将 arrayIndex 附加到嵌套映射。

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;

我也尝试过 const [rendered, setRendered] = useState(false); 之类的东西但没有成功,我尝试将其附加到 j 键。

如果我理解你的问题,你想迭代 first 字符串直到索引并在迭代字符串时显示“滚动”有趣的字符。

直觉上我认为更容易想到将 first 字符串的前面切片到索引,然后附加有趣的字符。

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'

棘手的问题是使用两个单独的 timers/intervals 来递增 first 字符串的索引并递增 fun characters 数组的索引。这是我想出的解决方案。

  1. 使用 React 引用来保存滚动有趣角色的间隔计时器引用。
  2. 单个 useEffect 挂钩开始“滚动”有趣的字符索引,按时间间隔递增。在 first 字符串字符数组递增时开始超时,如果仍有长度要迭代,则将另一个超时排队,否则 运行 清理函数以清除计时器和状态。
  3. first 字符串切片到索引 arrayIndex 并有条件地附加一个“滚动”有趣的字符。

代码:

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>;
};

演示