开发者问题收集

使用 React 和 TypeScript 在 Vite 中动态导入组件时快速刷新失败

2023-05-17
8516

我正在尝试创建一个多步骤表单,我将数据放在单独的文件中,用于保存此类常量

import { lazy } from 'react';

export const steps = [
  {
    id: 0,
    name: 'Personal Info',
    component: lazy(() => import('../components/PersonalInfo')),
  },
];

我将其传递给上下文中的自定义钩子

const dataSteps = useMultiStep(steps);

const { next, back, currentStep, isFirst } = dataSteps;

这是自定义钩子

import { useState } from 'react';
import { MultistepProps } from '../@types/Multiform';

const useMultiStep = (steps: MultistepProps[]) => {
  const [step, setStep] = useState(0);

  const isLast = step === steps?.length - 1;
  const isFirst = step === 0;

  const next = (): void => {
    if (isLast) return;
    setStep((current) => current + 1);
  };

  const back = (): void => {
    if (isFirst) return;
    setStep((current) => current - 1);
  };

  return {
    step,
    next,
    back,
    currentStep: steps[step],
    isFirst,
  };
};

export default useMultiStep;

我在这里使用动态组件

import FormInterface from './interface/FormInterface';
import useApp from './hooks/useApp';
import { Suspense } from 'react';
function App() {
  const data = useApp();

  const { currentStep, next, back, isFirst } = data;

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();

    next();
  };

  return (
    <FormInterface>
      <form
        onSubmit={handleSubmit}
        className="flex flex-col h-full py-5 group"
        noValidate={true}
      >
        {currentStep.component && (
          <>
            <h1 className="text-3xl font-bold text-marineBlue">
              {currentStep?.name}
            </h1>

            <Suspense fallback={<div>Loading...</div>}>
              <currentStep.component /> //here
            </Suspense>

            <div
              className={`mt-4 sm:mt-auto flex ${
                isFirst ? 'justify-end' : 'justify-between'
              }`}
            >
              <button
                type="button"
                className={`hover:text-marineBlue font-bold text-coolGray py-2 px-5 rounded-md text-[13px] ${
                  isFirst ? 'hidden' : 'block'
                }`}
                onClick={back}
              >
                Go Back
              </button>
              <button
                type="submit"
                className="hover:bg-purplishBlue bg-marineBlue text-white py-2 px-5 rounded-md text-[12px] group-invalid:pointer-events-none group-invalid:opacity-30 self-end"
              >
                Next Step
              </button>
            </div>
          </>
        )}
      </form>
    </FormInterface>
  );
}

export default App;

我的 vite 配置是这样的

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
});

尝试了所有方法后,仍然出现此错误,不是第一次渲染,而是在组件重新加载时

App.tsx:7 Uncaught TypeError: Cannot destructure property 'currentStep' of 'data' as it is null. at App (App.tsx:7:11) at renderWithHooks (react-dom.development.js:16305:18) at mountIndeterminateComponent (react-dom.development.js:20074:13) at beginWork (react-dom.development.js:21587:16) at HTMLUnknownElement.callCallback2 (react-dom.development.js:4164:14) at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16) at invokeGuardedCallback (react-dom.development.js:4277:31) at beginWork$1 (react-dom.development.js:27451:7) at performUnitOfWork (react-dom.development.js:26557:12) at workLoopSync (react-dom.development.js:26466:5)

我相信这是一个 HRM 问题,因为这就像加载整个页面而不是仅加载组件一样,因此状态会丢失,并且 useMultisteps 丢失了,但我就是找不到让它工作的方法,请帮我教我一个更好的方法来完成我想做的事情

1个回答

更新组件时,您的状态似乎丢失了(可能是因为 useApp() 钩子在数据准备好之前返回了 null )。

  1. 尝试将有状态组件包装在 React memo() 中。这样,您的组件状态将在 HMR 更新中保留。

例如,将 useApp 钩子包装在 useMemo 钩子中,以确保它只被调用一次:

const data = useMemo(() => useApp(), []);

总而言之,这将确保 useApp 钩子只被调用一次,并且其返回值被记忆,即使组件由于 HMR 而重新渲染。

  1. 第二个建议:尝试修改您的代码,如下所示:

    const { currentStep, next, back, isFirst } = data ?? {};

这将确保只有数据对象不为空时才会发生解构操作。

Ali Safari
2023-05-17