首页 > 解决方案 > 使用 JavaScript 使用 React.js 创建动画计数器

问题描述

我有四个计数器,我想使用 JavaScript 制作动画(将计数从 0 增加到特定数字)。我的代码如下:

const allCounters = document.querySelectorAll('.counterClass');
counters.forEach(allCounters => {
  const updateCounter = () => {
    const end = +allCounters.getAttribute('data-target');
    const count = +allCounters.innerText;
    const increment = end / 200;
    if (count < end) {
      allCounters.innerText = count + increment;
      setTimeout(updateCounter, 1);
    } else {
      allCounters.innerText = end;
    }
  };
  updateCounter();
});

React中,我不确定如何让它运行。我尝试在 using dangerouslySetInnerHTML之后包含代码,但这不起作用。(我是 React 的新手)。

我很感激你能给我的任何帮助。非常感谢!

就在我发布我的问题之前,我找到了一个可以做到这一点的插件(https://github.com/glennreyes/react-countup),但想知道是否仍然可以使用 JS。谢谢!

标签: javascriptreactjscounter

解决方案


在使用 React 时,尽量避免直接的 DOM 操作(查询和修改)。相反,让 React 完成 DOM 工作:

const Counter = ({start, end}) = {
  // useState maintains the value of a state across
  // renders and correctly handles its changes
  const {count, setCount} = React.useState(start);
  // useMemo only executes the callback when some dependency changes
  const increment = React.useMemo(() => end/200, [end]);

  // The logic of your counter
  // Return a callback to "unsubscribe" the timer (clear it)
  const doIncrement = () => {
    if(count < end) {
      const timer = setTimeout(
        () => setCount(
          count < (end - increment)
            ? count + increment
            : end
        ),
        1);
      return () => clearTimeout(timer);
    }
  }

  // useEffect only executes the callback once and when some dependency changes
  React.useEffect(doIncrement, [count, end, increment]);

  // Render the counter's DOM
  return (
    <div>{count}</div>
  )
}

const App = (props) => {
  // Generate example values:
  // - Generate 5 counters
  // - All of them start at 0
  // - Each one ends at it's index * 5 + 10
  // - useMemo only executes the callback once and
  //   when numCounters changes (here, never) 
  const numCounters = 5;
  const countersExample = React.useMemo(
    () => new Array(numCounters)
      .fill(0)
      .map( (c, index) => ({
        start: 0,
        end: index*5 + 10,
      })
    ),
    [numCounters]
  );

  return (
    <div id="counters-container">
      {
        // Map generated values to React elements
        countersExample
          .map( (counter, index) => <Counter key={index} {...counter}/> )
      }
    </div>
  )
}

推荐阅读