Last active
March 26, 2019 15:54
-
-
Save joenoon/bd60647a2ca9e96bc6bc3030c4807c13 to your computer and use it in GitHub Desktop.
needs some custom setup, but here is the idea
This file contains 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 {MobxReactLiteHooks} from './jn-mobx-lite'; | |
declare module 'jn-mobx-lite.macro'; | |
export function useObserver<T>(baseComponentName?: string): MobxReactLiteHooks; |
This file contains 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
const { createMacro } = require('babel-plugin-macros'); | |
const IMPORT_SOURCE = '@jn-mobx-lite'; | |
module.exports = createMacro(useObserverMacro); | |
function useObserverMacro({ | |
references, | |
state: { | |
file: { | |
opts: { filename }, | |
}, | |
}, | |
babel: { types: t, template }, | |
}) { | |
const { useObserver = [] } = references; | |
useObserver.forEach(referencePath => { | |
const program = referencePath.scope.getProgramParent().path; | |
const line = referencePath.getStatementParent(); | |
const lineVars = line.node.declarations | |
? line.node.declarations[0].id | |
: null; | |
const linesAfter = line.container.splice( | |
line.key + 1, | |
line.container.length - line.key - 1 | |
); | |
line.insertAfter( | |
t.returnStatement( | |
t.callExpression(t.identifier('useObserver'), [ | |
t.functionExpression( | |
t.identifier('useObserverRender'), | |
lineVars ? [lineVars] : [], | |
t.blockStatement(linesAfter), | |
false, | |
false | |
), | |
]) | |
) | |
); | |
line.remove(); | |
const foundImport = | |
program.get('body').filter(x => { | |
if (t.isImportDeclaration(x) && x.node.source.value === IMPORT_SOURCE) { | |
return !!x.node.specifiers.filter( | |
s => s.local.name === 'useObserverRender' | |
); | |
} | |
return false; | |
}).length > 0; | |
if (!foundImport) { | |
program.unshiftContainer( | |
'body', | |
t.importDeclaration( | |
[ | |
t.importSpecifier( | |
t.identifier('useObserver'), | |
t.identifier('useObserver') | |
), | |
], | |
t.stringLiteral(IMPORT_SOURCE) | |
) | |
); | |
} | |
}); | |
} |
This file contains 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 {useComputed, useObservable, useObserver as useObserverInternal} from 'mobx-react-lite'; | |
const HOOKS = { | |
useComputed, | |
useObservable, | |
}; | |
export type MobxReactLiteHooks = typeof HOOKS; | |
// This is for my convenience, not neccessary for the pattern. | |
export * from 'mobx'; | |
/** | |
* Any functional component needing observability or using useObservable/useComputed | |
* should be defined similar to: | |
* | |
* ``` | |
* export const MyComponent: React.FunctionComponent<{}> = props => | |
* useObserver(function useHooks({useObservable, useComputed}) { | |
* const someObservable = useObservable({foo: 10}); | |
* | |
* const myComputedVal = useComputed(() => { | |
* return someObservable.foo + 10; | |
* }); | |
* | |
* return ( | |
* <div> | |
* MyComponent: {myComputedVal} | |
* <button onClick={() => (someObservable.foo += 10)}>Bump</button> | |
* </div> | |
* ); | |
* }); | |
* ``` | |
* | |
* Enforcing this pattern and requiring `'useObservable` and `useComputed` are provided | |
* by a `useObserver` helps ensure expected behavior and the inner `useHooks` named | |
* function enables properly linting the rules of hooks. | |
* | |
* Note: `observer` HOC and the `Observer` component are not exposed. Components can be | |
* broken down into smaller units that can use `useObserver` with what React already | |
* provides us, simplifying the usage. | |
*/ | |
export function useObserver<T>(func: (hooks: MobxReactLiteHooks) => T, baseComponentName?: string): T { | |
return useObserverInternal(() => func(HOOKS), baseComponentName); | |
} |
This file contains 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
// Jest Snapshot v1, https://goo.gl/fbAQLP | |
exports[`macros 1. macros: 1. macros 1`] = ` | |
" | |
import {useObserver} from '../useObserver.macro' | |
export const MyComponent = (props) => { | |
console.log('here'); | |
const {useObservable, useComputed} = useObserver(); | |
const foo = useObservable({foo: 1}); | |
const bar = useComputed(() => foo.foo + 1); | |
return null; | |
}; | |
export const MyComponent2 = (props) => { | |
console.log('here'); | |
const {useObservable} = useObserver(); | |
const foo = useObservable({foo: 1}); | |
return null; | |
}; | |
export const MyComponent3 = (props) => { | |
console.log('here'); | |
useObserver(); | |
return null; | |
}; | |
↓ ↓ ↓ ↓ ↓ ↓ | |
import { useObserver } from \\"@jn-mobx-lite\\"; | |
export const MyComponent = props => { | |
console.log('here'); | |
return useObserver(function useObserverRender({ | |
useObservable, | |
useComputed | |
}) { | |
const foo = useObservable({ | |
foo: 1 | |
}); | |
const bar = useComputed(() => foo.foo + 1); | |
return null; | |
}); | |
}; | |
export const MyComponent2 = props => { | |
console.log('here'); | |
return useObserver(function useObserverRender({ | |
useObservable | |
}) { | |
const foo = useObservable({ | |
foo: 1 | |
}); | |
return null; | |
}); | |
}; | |
export const MyComponent3 = props => { | |
console.log('here'); | |
return useObserver(function useObserverRender() { | |
return null; | |
}); | |
}; | |
" | |
`; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment