Merge pull request #22159 from musicglue/recompose-withStateHandlers

[recompose]: Improve withHandler & withStateHandlers
This commit is contained in:
Mine Starks
2017-12-13 15:57:13 -08:00
committed by GitHub
2 changed files with 38 additions and 20 deletions

View File

@@ -78,13 +78,13 @@ declare module 'recompose' {
// withHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withhandlers
type EventHandler = Function;
type HandleCreators<TOutter> = {
[handlerName: string]: mapper<TOutter, EventHandler>;
type HandleCreators<TOutter, THandlers> = {
[handlerName in keyof THandlers]: mapper<TOutter, EventHandler>;
};
type HandleCreatorsFactory<TOutter, THandlers> = (initialProps: TOutter) => HandleCreators<TOutter>;
type HandleCreatorsFactory<TOutter, THandlers> = (initialProps: TOutter) => HandleCreators<TOutter, THandlers>;
export function withHandlers<TOutter, THandlers>(
handlerCreators: HandleCreators<TOutter> | HandleCreatorsFactory<TOutter, THandlers>
): InferableComponentEnhancerWithProps<THandlers, TOutter>;
handlerCreators: HandleCreators<TOutter, THandlers> | HandleCreatorsFactory<TOutter, THandlers>
): InferableComponentEnhancerWithProps<TOutter & THandlers, TOutter>;
// defaultProps: https://github.com/acdlite/recompose/blob/master/docs/API.md#defaultprops
export function defaultProps<T = {}>(
@@ -134,13 +134,16 @@ declare module 'recompose' {
// withStateHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withstatehandlers
type StateHandler<TState> = (...payload: any[]) => TState | undefined;
type StateUpdaters<TOutter, TState> = {
[updaterName: string]: (state: TState, props: TOutter) => StateHandler<TState>;
type StateHandlerMap<TState> = {
[updaterName: string]: StateHandler<TState>;
};
export function withStateHandlers<TState, TUpdaters, TOutter>(
type StateUpdaters<TOutter, TState, TUpdaters> = {
[updaterName in keyof TUpdaters]: (state: TState, props: TOutter) => StateHandler<TState>;
};
export function withStateHandlers<TState, TUpdaters extends StateHandlerMap<TState>, TOutter = {}>(
createProps: TState | mapper<TOutter, TState>,
stateUpdaters: StateUpdaters<TOutter, TState>,
): InferableComponentEnhancerWithProps<TUpdaters & TState, TOutter>;
stateUpdaters: StateUpdaters<TOutter, TState, TUpdaters>,
): InferableComponentEnhancerWithProps<TOutter & TState & TUpdaters, TOutter>;
// withReducer: https://github.com/acdlite/recompose/blob/master/docs/API.md#withReducer
type reducer<TState, TAction> = (s: TState, a: TAction) => TState;

View File

@@ -18,6 +18,8 @@ import {
createEventHandlerWithConfig,
componentFromStreamWithConfig, mapPropsStreamWithConfig,
setObservableConfig,
StateHandler,
StateHandlerMap,
} from "recompose";
import rxjsconfig from "recompose/rxjsObservableConfig";
import rxjs4config from "recompose/rxjs4ObservableConfig";
@@ -81,20 +83,20 @@ function testWithPropsOnChange() {
}
function testWithHandlers() {
interface OutterProps {
out: number;
}
interface InnerProps {
onSubmit: React.MouseEventHandler<HTMLDivElement>;
onChange: Function;
foo: string;
}
interface HandlerProps {
onSubmit: React.MouseEventHandler<HTMLDivElement>;
onChange: Function;
}
interface OutterProps { out: number; }
const InnerComponent: React.StatelessComponent<InnerProps> = ({onChange, onSubmit}) =>
<div onClick={onSubmit}></div>;
const InnerComponent: React.StatelessComponent<InnerProps & HandlerProps> = ({onChange, onSubmit, foo}) =>
<div onClick={onSubmit}>{foo}</div>;
const enhancer = withHandlers<OutterProps, HandlerProps>({
const enhancer = withHandlers<OutterProps & InnerProps, HandlerProps>({
onChange: (props) => (e: any) => {},
onSubmit: (props) => (e: React.MouseEvent<any>) => {},
});
@@ -106,7 +108,7 @@ function testWithHandlers() {
/>
)
const enhancer2 = withHandlers<OutterProps, HandlerProps>((props) => ({
const enhancer2 = withHandlers<OutterProps & InnerProps, HandlerProps>((props) => ({
onChange: (props) => (e: any) => {},
onSubmit: (props) => (e: React.MouseEvent<any>) => {},
}));
@@ -117,6 +119,11 @@ function testWithHandlers() {
out={42}
/>
)
const handlerNameTypecheckProof = withHandlers<OutterProps, HandlerProps>({
onChange: () => () => {},
notAKeyOnHandlerProps: () => () => {}, // $ExpectError
});
}
function testDefaultProps() {
@@ -186,11 +193,14 @@ function testWithState() {
function testWithStateHandlers() {
interface State { counter: number; }
interface Updaters { add: (n: number) => State; }
type InnerProps = State & Updaters;
interface Updaters extends StateHandlerMap<State> {
add: StateHandler<State>;
}
interface OutterProps { initialCounter: number, power: number }
type InnerProps = State & Updaters & OutterProps;
const InnerComponent: React.StatelessComponent<InnerProps> = (props) =>
<div>
<div>{`Initial counter: ${props.initialCounter}`}</div>
<div>{`Counter: ${props.counter}`}</div>
<div onClick={() => props.add(2)}></div>
</div>;
@@ -205,7 +215,12 @@ function testWithStateHandlers() {
const rendered = (
<Enhanced initialCounter={4} power={2} />
);
}
const updateNameTypecheckProof = withStateHandlers<State, Updaters, OutterProps>(
(props: OutterProps) => ({ counter: props.initialCounter }),
{ notAKeyOfUpdaters: (state, props) => n => ({ ...state, counter: state.counter + n ** props.power }), }, // $ExpectError
);
}
function testWithReducer() {
interface State { count: number }