@@ -4,40 +4,20 @@ import React, {
44 useCallback ,
55 useContext ,
66 useEffect ,
7- useMemo ,
87 useRef ,
98 useState ,
109} from 'react'
1110import { debounce } from 'lodash'
12- import {
13- useLocation ,
14- useNavigate ,
15- useParams ,
16- useSearchParams ,
17- } from 'react-router-dom'
11+ import { useSearchParams } from 'react-router-dom'
1812import { AnyZodObject } from 'zod'
1913
20- import { useConfig } from '@eplant/config'
21- import GeneInfoViewMetadata from '@eplant/views/GeneInfoView'
22-
23- import {
24- flattenObject ,
25- getStateFromParams ,
26- getViewIdFromPathname ,
27- getZodDefaults ,
28- } from './stateUtils'
29- import {
30- useActiveGeneId ,
31- useActiveViewId ,
32- useGeneticElements ,
33- useSpecies ,
34- } from '.'
14+ import { flattenObject , getStateFromParams , getZodDefaults } from './stateUtils'
15+ import { useActiveViewId } from '.'
3516
3617interface URLStateContext < T > {
3718 state : T | null
3819 setState : ( updatedState : T ) => void
3920 initializeState : ( schema : AnyZodObject ) => void
40- currentStateViewIdRef : React . MutableRefObject < string | null >
4121}
4222
4323const URLStateContext = createContext < URLStateContext < any > > ( {
@@ -48,105 +28,26 @@ const URLStateContext = createContext<URLStateContext<any>>({
4828 initializeState : ( ) => {
4929 throw new Error ( 'initializeState must be used within a URLStateProvider' )
5030 } ,
51- currentStateViewIdRef : { current : null } ,
5231} )
5332
5433export const URLStateProvider = ( { children } : { children : ReactNode } ) => {
34+ const [ activeViewId ] = useActiveViewId ( )
35+ const [ searchParams , setSearchParams ] = useSearchParams ( )
36+ const stateCache = useRef ( new Map < string , any > ( ) )
5537 const [ state , setState ] = useState < any > ( ( ) => {
5638 null
5739 } )
58- const [ activeViewId , setActiveViewId ] = useActiveViewId ( )
59- const [ searchParams , setSearchParams ] = useSearchParams ( )
60- const stateCache = useRef ( new Map < string , any > ( ) )
61- const currentStateViewIdRef = useRef < string | null > ( null )
62- const { views } = useConfig ( )
63- const location = useLocation ( )
64- const navigate = useNavigate ( )
65- const [ speciesList ] = useSpecies ( )
66- const [ genes , setGenes ] = useGeneticElements ( )
67- const [ activeGeneId , setActiveGeneId ] = useActiveGeneId ( )
68- const [ geneNotFound , setGeneNotFound ] = useState ( false )
69- const params = useParams ( )
70-
71- useEffect ( ( ) => {
72- const loadGene = async ( geneid : string ) => {
73- // TODO: This is super jank, should probably write some better utilities for loading genes
74- const species = speciesList . find (
75- ( species ) => species . name === 'Arabidopsis'
76- )
77- const newGene = await species ?. api . searchGene ( geneid )
78- if ( newGene ) {
79- setGenes ( [ ...genes , newGene ] )
80- } else {
81- setGeneNotFound ( true )
82- setActiveGeneId ( '' )
83- }
84- }
85- if ( params . geneid ) {
86- if ( params . geneid !== activeGeneId ) {
87- if ( ! genes . find ( ( g ) => g . id === params . geneid ) ) {
88- loadGene ( params . geneid )
89- }
90- if ( ! geneNotFound ) setActiveGeneId ( params . geneid )
91- }
92- } else {
93- // Set active gene to first available if one is already loaded
94- if ( genes . length > 0 ) {
95- setActiveGeneId ( genes [ 0 ] . id )
96- } else {
97- setActiveGeneId ( '' )
98- }
99- }
100-
101- // Set activeview
102- const urlView =
103- views . find ( ( view ) => view . id === location . pathname . split ( '/' ) [ 1 ] ) ??
104- GeneInfoViewMetadata
105-
106- setActiveViewId ( urlView . id )
107- } , [ ] )
108-
109- // On when the activegene or view changes, update path
110- useEffect ( ( ) => {
111- const oldPathSegments = location . pathname
112- . split ( '/' )
113- . filter ( ( segment ) => segment !== '' )
114-
115- const newPathSegments = [ ]
116- if ( activeViewId ) {
117- newPathSegments . push ( activeViewId )
118- }
119- if ( activeGeneId ) {
120- newPathSegments . push ( activeGeneId )
121- }
122-
123- if ( newPathSegments . length > 0 ) {
124- let newPath
125- if (
126- oldPathSegments . length > 0 &&
127- oldPathSegments [ 0 ] == newPathSegments [ 0 ]
128- ) {
129- // If the view is the same we want to retain query params in url, else we can wipe
130- // them and have URLStateManager handle things
131- newPath = '/' + newPathSegments . join ( '/' ) + location . search
132- } else {
133- newPath = '/' + newPathSegments . join ( '/' )
134- }
135- navigate ( newPath )
136- }
137- } , [ activeGeneId , activeViewId ] )
13840
13941 const debouncedUpdateSearchParams = useCallback (
14042 debounce ( ( updatedState : any ) => {
141- // Get current view ID from location to ensure correct caching
142- const currentViewId = getViewIdFromPathname ( location . pathname )
143- stateCache . current . set ( currentViewId , updatedState )
43+ // Setting component state
44+ stateCache . current . set ( activeViewId , updatedState )
14445
14546 // Setting params
14647 const flattenedState = flattenObject ( updatedState )
14748 setSearchParams ( new URLSearchParams ( flattenedState as any ) )
14849 } , 50 ) ,
149- [ location . pathname , setSearchParams ]
50+ [ activeViewId ]
15051 )
15152
15253 useEffect ( ( ) => {
@@ -160,25 +61,19 @@ export const URLStateProvider = ({ children }: { children: ReactNode }) => {
16061
16162 const initializeState = useCallback (
16263 < T extends AnyZodObject > ( schema : T ) => {
163- const currentViewId = getViewIdFromPathname ( location . pathname )
164-
165- const defaultState = getZodDefaults ( schema )
64+ // Get validated state
65+ const defaultState = getZodDefaults ( schema ) // This returns the defaults defined by Zod Schema
16666 const paramsState = getStateFromParams ( schema , searchParams )
167- const cachedState = stateCache . current . get ( currentViewId ) || { }
168-
67+ const cachedState = stateCache . current . get ( activeViewId ) || { }
16968 // Merge precedence: search params > cached state > defaults
17069 const mergedState = {
17170 ...defaultState ,
17271 ...cachedState ,
17372 ...paramsState ,
17473 }
175-
176- // Track which view ID this state belongs to
177- currentStateViewIdRef . current = currentViewId
178- setActiveViewId ( currentViewId )
17974 setState ( mergedState )
18075 } ,
181- [ searchParams , location . pathname ]
76+ [ searchParams , activeViewId ]
18277 )
18378
18479 return (
@@ -187,7 +82,6 @@ export const URLStateProvider = ({ children }: { children: ReactNode }) => {
18782 state,
18883 setState : ( updatedState ) => setState ( updatedState ) ,
18984 initializeState,
190- currentStateViewIdRef,
19185 } }
19286 >
19387 { children }
@@ -197,25 +91,8 @@ export const URLStateProvider = ({ children }: { children: ReactNode }) => {
19791
19892export const useURLState = < T , > ( ) => {
19993 const context = useContext ( URLStateContext )
200- const location = useLocation ( )
201-
20294 if ( ! context . setState ) {
20395 throw new Error ( 'useURLState must be used within a URLStateProvider' )
20496 }
205-
206- const currentUrlViewId = useMemo (
207- ( ) => getViewIdFromPathname ( location . pathname ) ,
208- [ location . pathname ]
209- )
210-
211- // Clear state to avoid
212- const safeState =
213- currentUrlViewId === context . currentStateViewIdRef . current
214- ? context . state
215- : null
216-
217- return {
218- ...context ,
219- state : safeState ,
220- } as URLStateContext < T >
97+ return context as URLStateContext < T >
22198}
0 commit comments