1- import { useRecoilValue } from 'recoil' ;
1+ import deepEqual from 'fast-deep-equal' ;
2+ import { useCallback , useEffect , useState } from 'react' ;
3+ import {
4+ selectorFamily ,
5+ useRecoilCallback ,
6+ useRecoilTransactionObserver_UNSTABLE ,
7+ } from 'recoil' ;
28
39import {
410 ExtendedDailyParticipant ,
5- participantPropertyState ,
11+ participantState ,
612} from '../DailyParticipants' ;
13+ import { RECOIL_PREFIX } from '../lib/constants' ;
714import type { NumericKeys } from '../types/NumericKeys' ;
815import type { Paths } from '../types/paths' ;
916import type { PathValue } from '../types/pathValue' ;
17+ import { resolveParticipantPaths } from '../utils/resolveParticipantPaths' ;
18+
19+ type PropertyType = {
20+ id : string ;
21+ properties : Paths < ExtendedDailyParticipant > [ ] ;
22+ } ;
23+
24+ /**
25+ * Stores resolved values for each participant and property path.
26+ */
27+ export const participantPropertyState = selectorFamily < any , PropertyType > ( {
28+ key : RECOIL_PREFIX + 'participant-property' ,
29+ get :
30+ ( { id, properties } ) =>
31+ ( { get } ) => {
32+ const participant = get ( participantState ( id ) ) ;
33+ return resolveParticipantPaths ( participant , properties ) ;
34+ } ,
35+ } ) ;
1036
1137type UseParticipantPropertyReturnType <
1238 T extends ExtendedDailyParticipant ,
@@ -29,16 +55,60 @@ export const useParticipantProperty = <
2955 participantId : string ,
3056 propertyPaths : P
3157) : UseParticipantPropertyReturnType < T , P > => {
32- const participantProperties = useRecoilValue (
33- participantPropertyState ( {
34- id : participantId ,
35- properties : ( Array . isArray ( propertyPaths )
36- ? propertyPaths
37- : [ propertyPaths ] ) as Paths < ExtendedDailyParticipant > [ ] ,
38- } )
58+ const [ properties , setProperties ] = useState < any [ ] > ( [ ] ) ;
59+
60+ /**
61+ * Updates properties state, in case the passed list of values differs to what's currently in state.
62+ */
63+ const maybeUpdateProperties = useCallback ( ( properties : any [ ] ) => {
64+ setProperties ( ( prevProperties ) => {
65+ if ( deepEqual ( properties , prevProperties ) ) return prevProperties ;
66+ return properties ;
67+ } ) ;
68+ } , [ ] ) ;
69+
70+ /**
71+ * Used to initialize the properties state, when the component mounts,
72+ * or the parameters change.
73+ */
74+ const initProperties = useRecoilCallback (
75+ ( { snapshot } ) =>
76+ async ( ) => {
77+ const properties = await snapshot . getPromise (
78+ participantPropertyState ( {
79+ id : participantId ,
80+ properties : ( Array . isArray ( propertyPaths )
81+ ? propertyPaths
82+ : [ propertyPaths ] ) as Paths < ExtendedDailyParticipant > [ ] ,
83+ } )
84+ ) ;
85+ maybeUpdateProperties ( properties ) ;
86+ } ,
87+ [ maybeUpdateProperties , participantId , propertyPaths ]
3988 ) ;
4089
41- return Array . isArray ( propertyPaths )
42- ? participantProperties
43- : participantProperties [ 0 ] ;
90+ /**
91+ * Effect to initialize state when mounted.
92+ */
93+ useEffect ( ( ) => {
94+ initProperties ( ) ;
95+ } , [ initProperties ] ) ;
96+
97+ /**
98+ * Asynchronously subscribes to updates to the participantPropertyState, without causing re-renders.
99+ * Anytime the recoil state returns a different list, we'll update this hook instance's state.
100+ */
101+ useRecoilTransactionObserver_UNSTABLE ( async ( { snapshot } ) => {
102+ const properties = await snapshot . getPromise (
103+ participantPropertyState ( {
104+ id : participantId ,
105+ properties : ( Array . isArray ( propertyPaths )
106+ ? propertyPaths
107+ : [ propertyPaths ] ) as Paths < ExtendedDailyParticipant > [ ] ,
108+ } )
109+ ) ;
110+ maybeUpdateProperties ( properties ) ;
111+ } ) ;
112+
113+ return Array . isArray ( propertyPaths ) ? properties : properties [ 0 ] ;
44114} ;
0 commit comments