如何在 Next js 中集成 React Odometerjs

How can I integrate React Odometerjs in Next js

非常遗憾,经过3天的尝试,我仍然无法将Odometer js集成到Next js中。我不明白我的代码哪里错了。这是我在 CodeSandBox 中的代码 - https://codesandbox.io/s/long-moon-z9zqu

这是代码-

export default function Home() {
  const [odometerValue, setOdometerValue] = useState(0);

  useEffect(() => {
    setTimeout(() => {
      setOdometerValue(300);
    }, 1000);
  }, []);

  return (
    <Odometer
      value={odometerValue}
      format="(,ddd)"
      theme="default"
    />
  );
}

我使用的 npm pacjage - https://www.npmjs.com/package/react-odometerjs

请看代码,谁能解决问题。对我很有帮助。

我认为问题在于它正在动态加载 react-odometerjs 库,因此第一次呈现 Home 组件时,尚未加载该库。所以它呈现 dynamic 选项的 loading 组件,它只显示一个 0。因为它是第一个呈现你的 useEffect 是 运行 并且设置 odometerValue 到300.

稍后,加载了 react-odometerjs 库,这导致 Home 重新呈现。 Home 渲染 Odometer 组件,但现在渲染真正的 Odometer 组件而不是 loading 组件,因此它会像里程表一样渲染,但第一次加载时该值设置为 300 , 所以它刚好是 300。

如果在将其设置为 300 之前添加一点延迟,那么您可以看到它正在运行。这是一个例子:

export default function Home() {
  const [odometerValue, setOdometerValue] = useState(0);

  useEffect(() => {
    setTimeout(() => {
      setOdometerValue(300);
    }, 1000);
  }, []);

  return (
    <Odometer
      value={odometerValue}
      format="(,ddd)"
      theme="default"
    />
  );
}

为此使用计时器的问题是实际加载库可能需要不同的时间。您需要使用 dynamic 加载此库的原因是因为您正在使用 nextjs 并且它正在进行服务器端渲染(SSR)并且当它进行 SSR 时它在没有 document 的 nodejs 中呈现或 window 定义了全局变量并且 odometer.js 使用它们。

所以你有一些选择:

  1. 使用 setTimeout 东西,如果库加载缓慢,会出现一些稍微烦人的行为。
  2. 使用另一个库来获得里程表效果。
  3. 分叉里程计库并删除它对 documentwindow 的依赖,然后你可以只使用 import {Odometer} from 'react-odometerjs' 而不是使用 dynamic.
  4. 实现一些让您知道何时在客户端加载了库,以便您可以在加载库后设置初始值。我在下面放了一个 hacky 版本。

设置前等待库加载的 Hacky 版本 odometerValue:

let loadedCallback = null;
let loaded = false;

const Odometer = dynamic(async () => {
  const mod = await import("react-odometerjs");
  loaded = true;
  if (loadedCallback != null) {
    loadedCallback();
  }
  return mod;
}, {
  ssr: false,
  loading: () => 0
});

export default function Home() {
  const [odometerLoaded, setOdometerLoaded] = useState(loaded);
  const [odometerValue, setOdometerValue] = useState(0);
    
  loadedCallback = () => {
    setOdometerLoaded(true);
  };

  useEffect(() => {
    if (odometerLoaded) {
      setOdometerValue(1);
    }
  }, [odometerLoaded]);

  useEffect(() => {
    setOdometerValue(300);
  }, [odometerValue]);

  return (
    <Odometer
      value={odometerValue}
      format="(,ddd)"
      theme="default"
    />
  );
}