Created
November 5, 2019 01:52
-
-
Save jpotterm/70ee7f556c73d99a2333c043530d4edf to your computer and use it in GitHub Desktop.
This file contains hidden or 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 * 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