search

Home  >  Q&A  >  body text

Title rewritten to: Dynamic component import in Vite with React and typescript causing failure in fast refresh

I'm trying to create a multi-step form and I'm putting the data in a separate file for constants like this

import {lazy} from "react";

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

I pass it to the custom hook in the context

const dataSteps = useMultiStep(steps);

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

This is a custom hook

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;

I am using dynamic components here

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;

My vite configuration is like this

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

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

After trying everything, I still get this error, not on the first render, but when the component reloads

App.tsx:7 Uncaught TypeError: Property 'currentStep' of 'data' cannot be destructured because it is null. In the application (App.tsx:7:11) in renderWithHooks (react-dom.development.js:16305:18) in mountInminatedComponent (react-dom.development.js:20074:13) At start working (react-dom.development.js:21587:16) at HTMLUnknownElement.callCallback2 (react-dom.development.js:4164:14) at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:16) in invokeGuardedCallback (react-dom.development.js:4277:31) at beginWork$1 (react-dom.development.js:27451:7) in PerformUnitOfWork (react-dom.development.js:26557:12) Working in LoopSync (react-dom.development.js:26466:5)

I believe this is an HRM issue because it's like loading the entire page with just the components because the state is lost and the information on useMultisteps is lost but I just can't find a way Ways to make it work, please help me and teach me a better way to accomplish what I want to do

P粉546179835P粉546179835426 days ago1064

reply all(1)I'll reply

  • P粉548512637

    P粉5485126372023-11-11 11:35:58

    Your state seems to be lost when you update the component (probably because the useApp() hook returns null before the data is ready).

    1. Try to wrap stateful components in React memo(). This way, your component state is preserved during HMR updates.

    For example wrap the useApp hook in a useMemo hook to ensure it is only called once:

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

    In summary, this will ensure that the useApp hook is only called once and its return value is remembered, even if the component is re-rendered due to HMR.

    1. Second suggestion: Try modifying your code like this:

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

    This will ensure that the destructuring operation only occurs when the data object is not empty.

    reply
    0
  • Cancelreply