Created
May 29, 2020 09:42
-
-
Save lkostrowski/3e92be00e31f7331ab67691d93a090fd to your computer and use it in GitHub Desktop.
Component resolver
This file contains 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 React, {ComponentType} from 'react'; | |
// Lib | |
declare function withDataResolving<TAppState, TReturnState, R1 = any>(injectSelectors: [(appState: TAppState) => R1], strategy: (data: R1) => TReturnState): <TProps extends {}>(Component: ComponentType<TProps>) => (props: TProps & TReturnState) => ComponentType<Exclude<TProps, TReturnState>>; | |
declare function withDataResolving<TAppState, TReturnState, R1 = any, R2 = any>(injectSelectors: [(appState: TAppState) => R1, (appState: TAppState) => R2], strategy: (data: R1, data2: R2) => TReturnState): <TProps extends {}>(Component: ComponentType<TProps>) => (props: TProps & TReturnState) => ComponentType<Exclude<TProps, TReturnState>>; | |
declare function withDataResolving<TAppState, TReturnState, R1 = any, R2 = any, R3 = any>(injectSelectors: [(appState: TAppState) => R1, (appState: TAppState) => R2, (appState: TAppState) => R3], strategy: (data: R1, data2: R2, data3: R3) => TReturnState): <TProps extends {}>(Component: ComponentType<TProps>) => (props: TProps & TReturnState) => ComponentType<Exclude<TProps, TReturnState>>; | |
// Example type | |
declare interface User { | |
name: string | |
} | |
// Example state-props for componanet | |
namespace UsersPageState { | |
export enum StateTag { | |
Loading, NoUsers, WithUsers, ErrorFetching | |
} | |
export type Loading = { | |
tag: StateTag.Loading | |
}; | |
export type WithNoUsers = { | |
tag: StateTag.NoUsers | |
} | |
export type WithUsers = { | |
tag: StateTag.WithUsers; | |
users: User[]; | |
} | |
export type ErrorFetching = { | |
tag: StateTag.ErrorFetching; | |
reason: string; | |
} | |
export type AllStates = Loading | WithUsers | WithNoUsers | ErrorFetching; | |
} | |
// Example app state | |
type AppState = { | |
users: User[]; | |
error?: Error; | |
fetchingState: 'pending' | 'fetched' | 'error'; | |
} | |
// Example redux selectors | |
declare module UsersSelector { | |
export function fetchingState(state: AppState): 'pending' | 'fetched' | 'error'; | |
export function getUsers(state: AppState): User[]; | |
export function fetchingError(state: AppState): Error[] | null; | |
} | |
// Example usage | |
const provideData = withDataResolving<AppState, UsersPageState.AllStates>( | |
[UsersSelector.fetchingState, UsersSelector.getUsers, UsersSelector.fetchingError], | |
(fetchingState, users, error) => { | |
if (fetchingState === 'pending') { | |
return { | |
tag: UsersPageState.StateTag.Loading | |
} | |
} | |
if (fetchingState === 'fetched' && users.length === 0) { | |
return { | |
tag: UsersPageState.StateTag.NoUsers, | |
} | |
} | |
if (fetchingState === 'fetched' && users.length > 0) { | |
return { | |
tag: UsersPageState.StateTag.WithUsers, | |
users, | |
} | |
} | |
if (fetchingState === 'error') { | |
return { | |
tag: UsersPageState.StateTag.ErrorFetching, | |
reason: error.message || 'Error fetching users' | |
} | |
} | |
throw Error() | |
}) | |
const UsersPage = (props: UsersPageState.AllStates) => { | |
const renderLoading = () => <div>loading</div>; | |
const renderEmpty = () => <div>no users</div> | |
const renderList = (users: User[]) => <div>{users.map(user => <div>{user.name}</div>)}</div> | |
switch (props.tag) { | |
case UsersPageState.StateTag.Loading: | |
return renderLoading(); | |
case UsersPageState.StateTag.NoUsers: | |
return renderEmpty(); | |
case UsersPageState.StateTag.WithUsers: | |
return renderList(props.users); | |
default: | |
throw Error(); | |
} | |
} | |
export const UsersPageWithData = provideData(UsersPage); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment