react-use-wizard

repository·main·Indexed 20 days ago

https://github.com/devrnt/react-use-wizard

A headless React wizard (stepper) builder version 2.3.0 that manages the logic of multi-step flows using hooks. It provides a Wizard component for layout and the useWizard hook for navigation control, including methods like nextStep, previousStep, and goToStep. The library supports asynchronous logic via handleStep, which manages an isLoading state during promise execution, and allows for custom UI and animations through a wrapper prop.

Tokens
3.5K
Snippets
13
Records
17
Agent score
69%

What's inside react-use-wizard

  1. Handle async logic with handleStep

    main

    You can attach asynchronous logic to a step using handleStep. This is useful for performing API calls or validations before proceeding to the next step.

    • If the handler is async or returns a Promise, isLoading will become true while the promise is pending.
    • If the handler throws an error, the wizard will stay on the current step and rethrow the error, allowing you to handle it with a try-catch block.
    • If the handler succeeds, the wizard automatically proceeds to the next step.
    const Step1 = () => {
      const { handleStep } = useWizard();
    
      // Async function
      handleStep(async () => {
        await fetch(...);
      });
    
      // OR
    
      // Return promise
      handleStep(() => {
        return fetch(...);
      });
    
      // ...
    }
  2. Quickstart: Create a basic wizard

    main

    To create a wizard, wrap your step components in the Wizard component. Inside each step component, use the useWizard hook to access navigation methods like nextStep and previousStep.

    Note: You cannot use useWizard in the same component where Wizard is defined; it must be used in a child component of Wizard.

    import * as React from 'react';
    import { Wizard, useWizard } from 'react-use-wizard';
    
    const App = () => (
      <Wizard>
        <Step1 />
        <Step2 />
        <Step3 />
      </Wizard>
    );
    
    const Step1 = () => {
      const { handleStep, previousStep, nextStep } = useWizard();
    
      // Attach an optional handler
      handleStep(() => {
        alert('Going to step 2');
      });
    
      return (
        <>
          <button onClick={() => previousStep()}>Previous ⏮️</button>
          <button onClick={() => nextStep()}>Next ⏭</button>
        </>
      );
    };
  3. Add animations to your wizard

    main

    Since react-use-wizard is logic-focused, you can use any animation library (like framer-motion) to animate step transitions. A common pattern is to use the wrapper prop on the Wizard component to wrap the active step in an animation component like <AnimatePresence />.

    // Example: Wrap steps in an <AnimatePresence> from framer-motion 
    const Wrapper = () => <AnimatePresence exitBeforeEnter />;
    
    const App = () => {
      return (
        <Wizard 
            header={<Header />}
            footer={<Footer />}
            wrapper={<Wrapper />}
          >
          <Step1 />
          <Step2 />
        </Wizard>
      );
    };
  4. View react-use-wizard examples in CodeSandbox

    main

    You can explore live, interactive implementations of react-use-wizard using the provided CodeSandbox links for different frameworks. These examples demonstrate how to integrate the wizard logic into specific environments like Gatsby and Next.js.

    | Name   | Link                                                                                |
    | ------ | ----------------------------------------------------------------------------------- |
    | Gatsby | https://codesandbox.io/s/github/devrnt/react-use-wizard/tree/main/examples/gatsby |
    | NextJS | https://codesandbox.io/s/github/devrnt/react-use-wizard/tree/main/examples/nextjs |
  5. Use the useWizard hook to control navigation

    main

    The useWizard hook provides methods and properties to manage the wizard state from within step components. Ensure the component calling this hook is a child of a Wizard component.

    const { 
      nextStep, 
      previousStep, 
      goToStep, 
      handleStep, 
      isLoading, 
      activeStep, 
      stepCount, 
      isFirstStep, 
      isLastStep 
    } = useWizard();
  6. Configure the Wizard component props

    main

    The Wizard component wraps your steps and provides layout structure. Each child component passed to Wizard is treated as an individual step.

    <Wizard 
        startIndex={0}
        header={<Header />}
        footer={<Footer />}
        wrapper={<Wrapper />}
      >
      <Step1 />
      <Step2 />
      <Step3 />
    </Wizard>
  7. Reference: useWizard methods and properties

    main

    Available properties and methods returned by the useWizard hook:

    | name | type | description |
    | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
    | nextStep | () => Promise<void> | Go to the next step |
    | previousStep | () => void | Go to the previous step index |
    | goToStep | (stepIndex: number) => void | Go to the given step index |
    | handleStep | (handler: Handler) => void | Attach a callback that will be called when calling `nextStep`. `handler` can be either sync or async |
    | isLoading | boolean | Will reflect the handler promise state: will be `true` if the handler promise is pending and `false` when the handler is either fulfilled or rejected |
    | activeStep | number | The current active step of the wizard |
    | stepCount | number | The total number of steps of the wizard |
    | isFirstStep | boolean | Indicate if the current step is the first step (aka no previous step) |
    | isLastStep | boolean | Indicate if the current step is the last step (aka no next step) |
  8. Reference: Wizard props

    main

    Available props for the Wizard component:

    | name | type | description | required | default |
    | ---------- | --------------- | ---------------------------------------------------------------------------------------------------------- | -------- | :--- |
    | startIndex | number | Indicate the wizard to start at the given step | ❌ | 0 |
    | header | React.ReactNode | Header that is shown above the active step | ❌ | |
    | footer | React.ReactNode | Footer that is shown below the active step | ❌ | |
    | onStepChange | (stepIndex) => void | Callback that will be invoked with the new step index when the wizard changes steps | ❌ | |
    | wrapper | React.ReactElement | Optional wrapper that is exclusively wrapped around the active step component. It is not wrapped around the `header` and `footer` | ❌ | |
    | children | React.ReactNode | Each child component will be treated as an individual step | ✔️ | |
  9. Use WizardValues from the useWizard hook

    main

    The useWizard hook returns a WizardValues object, which provides methods to control navigation and state, as well as status indicators for the current step and any active handlers.

    • nextStep(): Returns a Promise<void>. Moves to the next step.
    • previousStep(): Moves to the previous step.
    • goToStep(stepIndex: number): Moves to a specific step index (starting at 0).
    • handleStep(handler: Handler): Attaches a synchronous or asynchronous callback that will be executed when nextStep() is called.

    State Indicators

    • activeStep: The current active step index.
    • stepCount: The total number of steps.
    • isFirstStep: true if the current step is the first step.
    • isLastStep: true if the current step is the last step.
    • isLoading: true if a handler promise is currently pending; false if the handler is fulfilled or rejected.
    const {
      nextStep, 
      previousStep, 
      goToStep, 
      handleStep, 
      isLoading, 
      activeStep, 
      stepCount, 
      isFirstStep, 
      isLastStep
    } = useWizard();
  10. Attach async or sync handlers with handleStep

    main

    You can use handleStep to intercept the nextStep() call. This is useful for performing validation or saving data before allowing the user to proceed. The handler can be a synchronous function, an asynchronous function returning a Promise<void>, or null to remove the handler.

    While the handler is running, isLoading will be true.

    // Example: Async validation before proceeding
    handleStep(async () => {
      const isValid = await validateForm();
      if (!isValid) {
        throw new Error('Invalid form data');
      }
    });
    
    // To clear the handler
    handleStep(null);