Skip to content

Instantly share code, notes, and snippets.

@aselbie
Created November 9, 2018 00:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aselbie/b200eca8635cb6cbfaae784baa43ef60 to your computer and use it in GitHub Desktop.
Save aselbie/b200eca8635cb6cbfaae784baa43ef60 to your computer and use it in GitHub Desktop.
Buffet Custom Hooks Example
import { Context, useMemo } from 'react';
import { BaseRestaurant, NormalizedRestaurant, RestaurantDTO } from './ScopeSelector.types';
import { UserConsumer, State as UserContextState } from '@buffet/user-service';
import { RestaurantConsumer, State as RestaurantContextState } from '@buffet/restaurant-service';
import { normalizeRestaurant } from './RestaurantMapper';
export const LOCAL_STORAGE_KEY = 'gc.UserRecentRestaurants';
export const MAX_RECENT_RESTAURANTS = 5;
export interface RestaurantsByUserId {
[userId: string]: RestaurantDTO[];
}
function useContext<T>(context: Context<T>): T {
// Fake implementation
return 'foo' as any;
}
function useState<T>(initalValue: T): [T, (value: T) => void] {
return [initalValue, (T) => {}];
}
/**
* Our main export is a custom hook.
*/
export const useCurrentRestaurants = () => {
/**
* Get our context data. This will cause the component consuming our custom hook to rerender anytime the data in either context changes.
*/
const currentUserContext = useContext<UserContextState>(UserConsumer);
const currentRestaurantContext = useContext<RestaurantContextState>(RestaurantConsumer);
const userId =
currentUserContext.status === 'done' && currentUserContext.data ? currentUserContext.data.userName : undefined;
const currentRestaurant =
currentRestaurantContext.status === 'done' && currentRestaurantContext.data
? normalizeRestaurant(currentRestaurantContext.data)
: undefined;
/**
* Setup some state. We could probably read directly from localStorage, but if we did we would not be able to trigger a re-render with our `addRestaurant` function.
*/
const [restaurants, setRestaurants] = useState<NormalizedRestaurant[]>([]);
/**
* Helper function that first updates localStorage, then sets state to trigger a re-render.
*/
const setStateAndLocalStorage = (restaurants: NormalizedRestaurant[]) => {
setRestaurantsToLocalStorage(userId, restaurants);
setRestaurants(restaurants);
};
/**
* Initialize our data from localStorage if we have userId.
*/
const initializeRestaurantsFromLocalStorage = () => {
if (userId === undefined) return;
let updatedResaurants = getRestaurantsFromLocalStorageByUserId(userId);
if (currentRestaurant) {
updatedResaurants = addRestaurantToList(normalizeRestaurant(currentRestaurant), restaurants);
}
setStateAndLocalStorage(updatedResaurants);
};
// Only run our initialize function if userId or currentRestaurant has changed.
useMemo(initializeRestaurantsFromLocalStorage, [userId, currentRestaurant])
/**
* Create our bound add restaurant function
*/
const addRestaurant = (restaurant: NormalizedRestaurant): void => {
if (userId === undefined) {
console.warn('addRestaurant() was called without a userId. Aborting.');
return;
}
const updatedRestaurants = addRestaurantToList(restaurant, restaurants);
setStateAndLocalStorage(updatedRestaurants);
};
/**
* Return the interface of our hook.
*/
return {
restaurants,
addRestaurant
};
};
function getRestaurantsFromLocalStorageByUserId(userId: string): NormalizedRestaurant[] {
const restaurantsByUserId = getRestaurantsFromLocalStorage();
const restaurants = restaurantsByUserId[userId] || [];
return restaurants.map(normalizeRestaurant);
}
function getRestaurantsFromLocalStorage(): RestaurantsByUserId {
let restaurantsByUserId;
try {
restaurantsByUserId = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY) || '{}');
} catch (e) {
restaurantsByUserId = {};
}
return restaurantsByUserId;
}
function setRestaurantsToLocalStorage(userId: string, restaurants: NormalizedRestaurant[]): void {
const restaurantData = getRestaurantsFromLocalStorage();
restaurantData[userId] = restaurants;
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(restaurantData));
}
function addRestaurantToList<T extends BaseRestaurant>(restaurant: T, list: T[]): T[] {
return [restaurant].concat(list.filter(item => item.rid !== restaurant.rid)).slice(0, MAX_RECENT_RESTAURANTS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment