Created
November 9, 2018 00:55
-
-
Save aselbie/b200eca8635cb6cbfaae784baa43ef60 to your computer and use it in GitHub Desktop.
Buffet Custom Hooks Example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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