Skip to content

Instantly share code, notes, and snippets.

@jpotterm
Created November 5, 2019 01:52
Show Gist options
  • Save jpotterm/70ee7f556c73d99a2333c043530d4edf to your computer and use it in GitHub Desktop.
Save jpotterm/70ee7f556c73d99a2333c043530d4edf to your computer and use it in GitHub Desktop.
import * as React from 'react';
import * as mobx from 'mobx';
import * as mobxReactLite from 'mobx-react-lite';
type LooseProps = {
observable: any;
property: any;
childType: React.FunctionComponent<any>;
childProps: any;
};
type StrictProps<A extends object, B extends keyof A, C extends object> = {
observable: A;
property: B;
childType: React.FunctionComponent<C>;
childProps: Omit<C, 'value' | 'onChange'>;
};
const h = React.createElement;
function shallowEqual(a: any, b: any): boolean {
if (Object.is(a, b)) return true;
if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) return false;
const aKeys = Object.keys(a);
const bKeys = Object.keys(b);
if (aKeys.length !== bKeys.length) return false;
for (const aKey of aKeys) {
if (!b.hasOwnProperty(aKey) || !Object.is(a[aKey], b[aKey])) return false;
}
return true;
}
function propsEqual(a: any, b: any) {
if (!shallowEqual(a, b)) return false;
if (typeof a.childProps === 'object' && a.childProps !== null && !shallowEqual(a, b)) return false;
return true;
}
const TwoWayBinding = React.memo((props: LooseProps) => {
const onChange = React.useCallback(
(value) => {
mobx.runInAction(() => {
props.observable[props.property] = value;
});
},
[props.observable, props.property],
);
return mobxReactLite.useObserver(() => {
return h(props.childType, {
...props.childProps,
value: props.observable[props.property],
onChange: onChange,
});
});
}, propsEqual);
// Usage:
// createTwoWayBinding({
// observable: foo,
// property: 'bar',
// childType: Select,
// childProps: {
// options: [
// { title: 'One', value: 'one' },
// { title: 'Two', value: 'two' },
// ],
// },
// });
export function createTwoWayBinding<A extends object, B extends keyof A, C extends object>(props: StrictProps<A, B, C>) {
return h(TwoWayBinding, {
observable: props.observable,
property: props.property,
childType: props.childType,
childProps: props.childProps,
});
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment