You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
52 lines
1.5 KiB
52 lines
1.5 KiB
import * as React from "react"; |
|
|
|
import { AppState } from "@client/state"; |
|
|
|
const StateContext = React.createContext<AppState | null>(null); |
|
|
|
export interface ProvideStateProps { |
|
state: AppState; |
|
children: React.ReactNode; |
|
} |
|
|
|
export function ProvideState({ state, children }: ProvideStateProps) { |
|
return ( |
|
<StateContext.Provider value={state}>{children}</StateContext.Provider> |
|
); |
|
} |
|
|
|
export interface ConsumeStateProps { |
|
children: (state: AppState) => React.ReactNode; |
|
} |
|
|
|
export function ConsumeState({ children }: ConsumeStateProps) { |
|
const consumeState = (state: AppState | null) => { |
|
if (state == null) { |
|
throw new Error( |
|
"Component with ConsumeState must be mounted inside ProvideState" |
|
); |
|
} |
|
return children(state); |
|
}; |
|
return <StateContext.Consumer>{consumeState}</StateContext.Consumer>; |
|
} |
|
|
|
export function injectState<P extends { appState: AppState }>( |
|
Component: React.ComponentType<P> |
|
): React.ComponentClass<Omit<P, "appState">> { |
|
return class extends React.Component<Omit<P, "appState">> { |
|
render() { |
|
const consumeState = (state: AppState | null) => { |
|
if (state == null) { |
|
throw new Error( |
|
"Component with injectState must be mounted inside ProvideState" |
|
); |
|
} |
|
// tslint:disable-next-line:no-object-literal-type-assertion |
|
const allProps: Readonly<P> = {...this.props, appState: state} as Readonly<P>; |
|
return <Component {...allProps} />; |
|
}; |
|
return <StateContext.Consumer>{consumeState}</StateContext.Consumer>; |
|
} |
|
}; |
|
}
|
|
|