Skip to content

toss/use-funnel

Repository files navigation

@use-funnel

A powerful and safe step-by-step state management library

@use-funnel is a React Hook that helps you easily implement complex UI flows.

Core Concepts

Strong Type Support

By comparing the type of the current step with the next, you can ensure that only the required states are managed safely.

State Management by History

Manage states based on history, making it easy to handle backward and forward navigation.

Various Router Support

Supports browser history, react-router-dom, next.js, @react-navigation/native, and more.

Example

use-funnel-example.mp4
import { useFunnel } from '@use-funnel/react-router-dom';

export function App() {
  const funnel = useFunnel<{
    SelectJob: { jobType?: 'STUDENT' | 'EMPLOYEE' };
    SelectSchool: { jobType: 'STUDENT'; school?: string };
    SelectEmployee: { jobType: 'EMPLOYEE'; company?: string };
    EnterJoinDate: { jobType: 'STUDENT'; school: string } | { jobType: 'EMPLOYEE'; company: string };
    Confirm: ({ jobType: 'STUDENT'; school: string } | { jobType: 'EMPLOYEE'; company: string }) & { joinDate: string };
  }>({
    id: 'hello-world',
    initial: {
      step: 'SelectJob',
      context: {},
    },
  });
  return (
    <funnel.Render
      SelectJob={funnel.Render.with({
        events: {
          selectSchool: (_, { history }) => history.push('SelectSchool', { jobType: 'STUDENT' }),
          selectEmployee: (_, { history }) => history.push('SelectEmployee', { jobType: 'EMPLOYEE' }),
        },
        render({ dispatch }) {
          return (
            <SelectJob
              onSelectSchool={() => dispatch('selectSchool')}
              onSelectEmployee={() => dispatch('selectEmployee')}
            />
          );
        },
      })}
      SelectSchool={({ history }) => (
        <SelectSchool 
          onNext={(school) => 
            history.push('EnterJoinDate', (prev) => ({ 
              ...prev, 
              school 
            }))
          } 
        />
      )}
      SelectEmployee={({ history }) => (
        <SelectEmployee
          onNext={(company) =>
            history.push('EnterJoinDate', (prev) => ({
              ...prev,
              company,
            }))
          }
        />
      )}
      EnterJoinDate={funnel.Render.overlay({
        render({ history, close }) {
          return (
            <EnterJoinDateBottomSheet
              onNext={(joinDate) => history.push('Confirm', { joinDate })}
              onClose={() => close()}
            />
          );
        },
      })}
      Confirm={({ context }) =>
        context.jobType === 'STUDENT' ? (
          <ConfirmStudent school={context.school} joinDate={context.joinDate} />
        ) : (
          <ConfirmEmployee company={context.company} joinDate={context.joinDate} />
        )
      }
    />
  );
}

declare function SelectJob(props: { onSelectSchool(): void; onSelectEmployee(): void }): JSX.Element;
declare function SelectSchool(props: { onNext(school: string): void }): JSX.Element;
declare function SelectEmployee(props: { onNext(company: string): void }): JSX.Element;
declare function EnterJoinDateBottomSheet(props: { onNext(joinDate: string): void; onClose(): void }): JSX.Element;
declare function ConfirmStudent(props: { school: string; joinDate: string }): JSX.Element;
declare function ConfirmEmployee(props: { company: string; joinDate: string }): JSX.Element;

Visit use-funnel.slash.page for docs, guides, API and more!

English | 한국어

Contributing

Read our Contributing Guide to familiarize yourself with @use-funnel development process, how to suggest bug fixes and improvements, and the steps for building and testing your changes.

Toss

MIT © Viva Republica, Inc. See LICENSE for details.