Skip to content

Instantly share code, notes, and snippets.

@petejodo
Created September 21, 2017 16:37
Show Gist options
  • Select an option

  • Save petejodo/dedd0b765239e16f0fdb87ca2d3eb843 to your computer and use it in GitHub Desktop.

Select an option

Save petejodo/dedd0b765239e16f0fdb87ca2d3eb843 to your computer and use it in GitHub Desktop.
Merge Theme React HOC using Flow
// These are the things that have worked from my experimentation
import React from 'react';
import MyWrappedComponent from './MyWrappedComponent';
// $ExpectError - missing `someProp` which is required, (good)
<MyWrappedComponent />;
<MyWrappedComponent someProp={1} />; // (good)
<MyWrappedComponent someProp={1} theme={{}} />; // an empty object is fine since it's still a shape of Theme (good)
<MyWrappedComponent someProp={1} theme={{ someClass: 'my-class' }} />; // (good)
// $ExpectError - `anotherClass` isn't a property in the shape of Theme (good)
<MyWrappedComponent someProp={1} theme={{ anotherClass: 'another-class' }} />;
<MyWrappedComponent someProp={1} anotherProp="foo" />; // supposedly normal behavior (good)
// @flow
import * as React from 'react';
import classnames from 'classnames';
import type {HigherOrderComponent} from 'react-flow-types';
type Theme = { [className: string]: string };
export type MergeThemeHOC<T: Theme> = T => HigherOrderComponent<{theme?: $Shape<T>}, {theme: T}>;
const mergeTheme: MergeThemeHOC<*> = (injectedTheme: Theme) => (Component: any): any => {
return (props) => {
let theme: Theme = injectedTheme;
if (props && props.theme) {
const passedTheme: $Shape<Theme> = props.theme;
theme = Object.keys(passedTheme)
.filter((key: string) => !!injectedTheme[key])
.reduce((accum: Theme, key: string) => {
accum[key] = classnames(passedTheme[key], injectedTheme[key]);
return accum;
}, { ...passedTheme, ...injectedTheme });
}
return <Component {...props} theme={theme} />;
};
};
// @flow
import React from 'react';
type MyComponentTheme = {
someClass: string
};
export type {MyComponentTheme as Theme};
type MyComponentProps = {
someProp: number,
theme: MyComponentTheme
};
export default (props: MyComponentProps) => <div>{/* ... */}</div>;
// @flow
import Component from './MyComponent';
import type {Theme} from './MyComponent';
import mergeTheme from './mergeTheme';
import type {MergeThemeHOC} from './mergeTheme';
// BUILT CSS
var style = {
someClass: 'MyComponent__someClass___rD0by' // Commenting this line out will throw a flow error which is good
}; // Adding an additional property with type string to this object won't throw an error though which is not preferable but also not a deal breaker
const merge: MergeThemeHOC<Theme> = mergeTheme;
const MyWrappedComponent = merge(style)(Component);
MyWrappedComponent.displayName = 'MyWrappedComponent';
export default MyWrappedComponent;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment