--- description: React state management guidelines for Twenty CRM alwaysApply: false --- # React State Management ## Jotai Patterns ```typescript // ✅ Atoms for primitive state (use createAtomState for keyed state with optional persistence) import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState'; export const currentUserState = createAtomState({ key: 'currentUserState', defaultValue: null, }); // ✅ Derived atoms for computed state (use createAtomSelector) import { createAtomSelector } from '@/ui/utilities/state/jotai/utils/createAtomSelector'; export const userDisplayNameSelector = createAtomSelector({ key: 'userDisplayNameSelector', get: ({ get }) => { const user = get(currentUserState); return user ? `${user.firstName} ${user.lastName}` : 'Guest'; }, }); // ✅ Atom factory pattern for dynamic atoms (use createAtomFamilyState) import { createAtomFamilyState } from '@/ui/utilities/state/jotai/utils/createAtomFamilyState'; export const userByIdState = createAtomFamilyState({ key: 'userByIdState', defaultValue: null, }); ``` ## Jotai Hooks ```typescript // useAtomState - read and write import { useAtomState } from '@/ui/utilities/state/jotai/hooks/useAtomState'; // useAtomStateValue - read only import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue'; // useSetAtomState - write only import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState'; ``` ## Provider Jotai works without a Provider by default. For scoped stores or testing, use `Provider` from `jotai`. ## Local State Guidelines ```typescript // ✅ Multiple useState for unrelated state const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [data, setData] = useState([]); // ✅ useReducer for complex state logic type FormAction = | { type: 'SET_FIELD'; field: string; value: string } | { type: 'SET_ERRORS'; errors: Record } | { type: 'RESET' }; const formReducer = (state: FormState, action: FormAction): FormState => { switch (action.type) { case 'SET_FIELD': return { ...state, [action.field]: action.value }; case 'SET_ERRORS': return { ...state, errors: action.errors }; case 'RESET': return initialFormState; default: return state; } }; ``` ## Data Flow Rules - **Props down, events up** - Unidirectional data flow - **Avoid bidirectional binding** - Use callback functions - **Normalize complex data** - Use lookup tables over nested objects ```typescript // ✅ Normalized state structure type UsersState = { byId: Record; allIds: string[]; }; // ✅ Functional state updates const increment = useCallback(() => { setCount(prev => prev + 1); }, []); ``` ## Performance Tips - Use atom factory pattern (createAtomFamilyState) for dynamic data collections - Derived atoms (createAtomSelector) are automatically memoized by Jotai - Avoid heavy computations in derived atoms - Batch state updates when possible