State Management
State Management
State Management Patterns
Web (React + Apollo)
// Example React component using Apollo's `useQuery` hook
import { useQuery, gql } from '@apollo/client';
const GET_OBJECTS = gql`
query getMyObjects {
myObjects {
id
name
main_image_url
}
}
`;
export const ObjectList = () => {
const { data, loading, error } = useQuery(GET_OBJECTS);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.myObjects.map((o: any) => (
<li key={o.id}>{o.name}</li>
))}
</ul>
);
};
State Management Implementation
The logic for optimistic updates resides within the Apollo Client cache (or an additional Zustand store if more complex local state is required).
// Conceptual React hook for an optimistic moveObject mutation
import { gql, useMutation } from '@apollo/client';
const MOVE_OBJECT = gql`
mutation MoveObject($objectId: ID!, $newContainerId: ID!) {
moveObject(objectId: $objectId, newContainerId: $newContainerId) {
id
containerId
}
}
`;
export const useMoveObject = () => {
const [mutate, { error }] = useMutation(MOVE_OBJECT, {
optimisticResponse: ({ objectId, newContainerId }) => ({
moveObject: {
__typename: 'SpatialObject',
id: objectId,
containerId: newContainerId,
},
}),
update(cache, { data: { moveObject } }) {
// Update any necessary queries in cache here
},
});
return { moveObject: mutate, error };
};
Technology Stack
Web Application
- Framework: React 18 (function components + hooks)
- GraphQL Client: Apollo Client (or similar)
- State Management: React Context + Apollo Client cache (optionally Zustand)
- UI Components: Tailwind CSS + Custom Design System
- Build Tool: Vite (via CDN for gradual migration)
- PWA: Service Worker for offline support
Native Mobile Apps (Future)
- Platform: Native iOS (Swift) / Android (Kotlin)
- GraphQL Client: Native Apollo Client
- State Management: Platform-native reactive patterns
Status State Management
Status Definitions Cache
Status definitions should be loaded once on app startup and cached for the session:
// Global status state hook
export const useStatusDefinitions = () => {
const { data, loading, error } = useQuery(GET_STATUS_DEFINITIONS, {
// Cache for the entire session
fetchPolicy: 'cache-first',
// Persist in local storage for offline usage
notifyOnNetworkStatusChange: false
});
const statusByKey = useMemo(() =>
data?.statusDefinitions?.reduce((acc, status) => {
acc[status.key] = status;
return acc;
}, {}) || {}, [data]
);
const statusesByCategory = useMemo(() =>
data?.statusDefinitions?.reduce((acc, status) => {
if (!acc[status.category]) acc[status.category] = [];
acc[status.category].push(status);
return acc;
}, {}) || {}, [data]
);
return {
statusDefinitions: data?.statusDefinitions || [],
statusByKey,
statusesByCategory,
loading,
error
};
};
Status Validation State
// Status validation hook with debouncing
export const useStatusValidation = () => {
const [validationState, setValidationState] = useState({
isValidating: false,
result: null,
error: null
});
const [validateStatusCombination] = useLazyQuery(VALIDATE_STATUS_COMBINATION, {
onCompleted: (data) => {
setValidationState({
isValidating: false,
result: data.validateStatusCombination,
error: null
});
},
onError: (error) => {
setValidationState({
isValidating: false,
result: null,
error
});
}
});
const validate = useCallback(
debounce((statusKeys: string[]) => {
setValidationState(prev => ({ ...prev, isValidating: true }));
validateStatusCombination({ variables: { statusKeys } });
}, 300),
[validateStatusCombination]
);
return { validationState, validate };
};
Object Status Updates
// Optimistic status update hook
export const useUpdateObjectStatus = () => {
const [updateObject] = useMutation(UPDATE_OBJECT_STATUS, {
optimisticResponse: ({ objectId, statuses }) => ({
updateObject: {
__typename: 'ObjectInstance',
id: objectId,
statuses
}
}),
update(cache, { data: { updateObject } }) {
// Update any cached object queries
cache.modify({
id: cache.identify(updateObject),
fields: {
statuses() {
return updateObject.statuses;
}
}
});
}
});
return updateObject;
};