1- import { useMemo , useState , useEffect , useCallback } from 'react' ;
1+ import { useMemo , useState , useEffect , useCallback , useRef } from 'react' ;
22import { useSearchParams , useRouter , usePathname } from 'next/navigation' ;
33import { useAtom } from 'jotai/react' ;
44import { UNDO } from 'jotai-history' ;
@@ -10,12 +10,12 @@ import {
1010 getFunnelStepByPosition ,
1111 funnelPositionHistoryAtom ,
1212} from '../store/funnelStore' ;
13- import type { FunnelSession } from '../types/funnelBoot ' ;
13+ import { useToggle } from '../../../hooks/useToggle ' ;
1414
1515interface UseFunnelNavigationProps {
1616 funnel : FunnelJSON ;
17+ initialStepId ?: string | null ;
1718 onNavigation : TrackOnNavigate ;
18- session : FunnelSession ;
1919}
2020
2121type Chapters = Array < { steps : number } > ;
@@ -32,6 +32,7 @@ interface HeaderNavigation {
3232
3333export interface UseFunnelNavigationReturn {
3434 chapters : Chapters ;
35+ isReady : boolean ;
3536 navigate : NavigateFunction ;
3637 position : FunnelPosition ;
3738 step : FunnelStep ;
@@ -74,8 +75,8 @@ function updateURLWithStepId({
7475
7576export const useFunnelNavigation = ( {
7677 funnel,
78+ initialStepId,
7779 onNavigation,
78- session,
7980} : UseFunnelNavigationProps ) : UseFunnelNavigationReturn => {
8081 const router = useRouter ( ) ;
8182 const searchParams = useSearchParams ( ) ;
@@ -84,6 +85,9 @@ export const useFunnelNavigation = ({
8485 const [ position , setPosition ] = useAtom ( funnelPositionAtom ) ;
8586 const [ history , dispatchHistory ] = useAtom ( funnelPositionHistoryAtom ) ;
8687 const isFirstStep = ! position . step && ! position . chapter ;
88+ const isInitialized = useRef < boolean > ( false ) ;
89+ const [ isReady , setIsReady ] = useToggle ( false ) ;
90+ const urlStepId = searchParams . get ( 'stepId' ) ;
8791
8892 const chapters : Chapters = useMemo (
8993 ( ) => funnel . chapters . map ( ( chapter ) => ( { steps : chapter . steps . length } ) ) ,
@@ -92,6 +96,16 @@ export const useFunnelNavigation = ({
9296
9397 const stepMap : StepMap = useMemo ( ( ) => getStepMap ( funnel ) , [ funnel ] ) ;
9498
99+ const setPositionById = useCallback (
100+ ( stepId : FunnelStep [ 'id' ] ) => {
101+ const newPosition = stepMap [ stepId ] ?. position ;
102+ if ( newPosition ) {
103+ setPosition ( newPosition ) ;
104+ }
105+ } ,
106+ [ setPosition , stepMap ] ,
107+ ) ;
108+
95109 const step : FunnelStep = useMemo (
96110 ( ) => getFunnelStepByPosition ( funnel , position ) ,
97111 [ funnel , position ] ,
@@ -111,8 +125,7 @@ export const useFunnelNavigation = ({
111125 }
112126
113127 // update the position in the store
114- const newPosition = stepMap [ to ] ?. position ;
115- setPosition ( newPosition ) ;
128+ setPositionById ( to ) ;
116129
117130 // track the navigation event
118131 onNavigation ( { from, to, timeDuration, type } ) ;
@@ -128,7 +141,7 @@ export const useFunnelNavigation = ({
128141 pathname ,
129142 router ,
130143 searchParams ,
131- setPosition ,
144+ setPositionById ,
132145 step ,
133146 stepMap ,
134147 stepTimerStart ,
@@ -160,22 +173,43 @@ export const useFunnelNavigation = ({
160173 [ isFirstStep , step ?. transitions ] ,
161174 ) ;
162175
163- useEffect (
164- ( ) => {
165- // Check if the URL has a stepId parameter or if there is a session
166- const stepId = searchParams . get ( 'stepId' ) ?? session . currentStep ;
176+ // On load: Update the initial position in state and URL
177+ useEffect ( ( ) => {
178+ if ( isInitialized . current ) {
179+ return ;
180+ }
167181
168- if ( ! stepId ) {
169- return ;
170- }
182+ if ( initialStepId ) {
183+ setPositionById ( initialStepId ) ;
184+ }
171185
172- const newPosition = stepMap [ stepId ] ?. position ;
173- setPosition ( newPosition ) ;
174- } ,
175- // only run on mount
176- // eslint-disable-next-line react-hooks/exhaustive-deps
177- [ funnel . id ] ,
178- ) ;
186+ updateURLWithStepId ( {
187+ router,
188+ pathname,
189+ searchParams,
190+ stepId : initialStepId || step . id ,
191+ } ) ;
192+
193+ isInitialized . current = true ;
194+ setIsReady ( true ) ;
195+ } , [
196+ initialStepId ,
197+ pathname ,
198+ router ,
199+ searchParams ,
200+ setPositionById ,
201+ step . id ,
202+ setIsReady ,
203+ ] ) ;
204+
205+ // After load: update the position when the URL's stepId changes
206+ useEffect ( ( ) => {
207+ if ( ! urlStepId || ! isInitialized . current ) {
208+ return ;
209+ }
210+
211+ setPositionById ( urlStepId ) ;
212+ } , [ setPositionById , urlStepId ] ) ;
179213
180214 return {
181215 back,
@@ -184,5 +218,6 @@ export const useFunnelNavigation = ({
184218 position,
185219 skip,
186220 step,
221+ isReady,
187222 } ;
188223} ;
0 commit comments