View Docs
Migrating from @toss/use-funnel

Migrating from @toss/use-funnel

This document explains how to migrate code from @toss/use-funnel (opens in a new tab) to the new @use-funnel. Since @toss/use-funnel was written for Next.js Page Router, this document focuses on migrating to @use-funnel/next.

New Benefits

  • Fixed issues where state did not follow history (related document (opens in a new tab))
  • Allows defining state for each step
  • Supports not only Next.js but also react-router and @react-navigation/native

Installation

See Installation (opens in a new tab) for more details.

npm install @use-funnel/next --save

Breaking Changes

Migrating Steps

The new @use-funnel allows defining not just the steps but also the state for each step. If your funnel in @toss/use-funnel does not require different states for each step, you can use createFunnelSteps() provided by @use-funnel.

Before

import { useFunnel } from '@toss/use-funnel';
 
const FUNNEL_STEPS = ['A', 'B', 'C'] as const;
const [Funnel, setFunnel] = useFunnel(FUNNEL_STEPS);
 

After

import { createFunnelSteps } from '@use-funnel/next';
 
const steps = createFunnelSteps()
  .extends(['A', 'B', 'C'])
  .build();
 
const funnel = useFunnel({
  steps,
  initial: {
    step: 'A',
    context: {}
  }
});

Migrating <Funnel />, <Funnel.Step /> Components

In @use-funnel, you no longer need separate components to infer state types for each step. Instead, you can use funnel.step and funnel.context returned by useFunnel() and render based on switch-case statements.

Before

import { useFunnel } from '@toss/use-funnel';
 
const [Funnel, setFunnel] = useFunnel([
'A', 'B'
] as const);
 
return (
  <Funnel>
    <Funnel.Step name="A">
      <div>A</div>
    </Funnel.Step>
    <Funnel.Step name="B">
      <div>B</div>
    </Funnel.Step>
  </Funnel>
)

After

import {useFunnel} from '@use-funnel/next';
 
const steps = createFunnelSteps().extends(['A', 'B']).build();
const funnel = useFunnel({
  steps,
  initial: {
    step: 'A',
    context: {}
  }
});
 
switch (funnel.step) {
  case 'A':
    return <div>A</div>;
  case 'B':
    return <div>B</div>;
}
 

Alternatively, you can use <funnel.Render /> for a more declarative approach. See the funnel.Render component for more details.

Migrating withState()

If you were using withState() in @toss/use-funnel, you can now achieve the same functionality by passing a generic to createFunnelSteps().

Before

import { useFunnel } from '@toss/use-funnel';
 
const [Funnel, state, setState] = useFunnel([
  'A', 'B', 'C'
] as const).withState<{
  foo?: string;
  bar?: number;
}>({
  foo: 'Hello',
  bar: 5
});
 

After

import { useFunnel, createFunnelSteps } from '@use-funnel/next';
 
const steps = createFunnelSteps<{
  foo?: string;
  bar?: number;
}>()
  .extends(['A', 'B', 'C'])
  .build();
 
const funnel = useFunnel({
  steps,
  initial: {
    step: 'A',
    context: {
      foo: 'Hello',
      bar: 5,
    },
  },
});