Babel plugin/macro offering a pleasant syntax for rendering children on demand.
A React component added to the tree will always evaluate all of its properties including the component body. A simple example of this (taken from jsx-control-statements) is attempting to implement a conditional component:
<IfComponent condition={item}>
<Text>{item.title}</Text>
</IfComponent>
The error will be "Cannot read property 'title' of undefined", because React will evaluate the body of the custom component and pass it as "children" property to it. The only workaround is to force React into lazy evaluation by wrapping the statement in a function.
<IfComponent condition={item} render={() => (<Text>{item.title}</Text>)}></IfComponent>
This is precisely what this plugin/macro does, albeit with a slightly different syntax:
<AsyncHandler status={status}>
<JsxOnDemandChildren>
<AsyncHandler.Loading>...</AsyncHandler.Loading>
<AsyncHandler.Success>...</AsyncHandler.Success>
<AsyncHandler.Error>...</AsyncHandler.Error>
</JsxOnDemandChildren>
</AsyncHandler>
becomes:
<AsyncHandler
Loading={() => <AsyncHandler.Loading>...</AsyncHandler.Loading>}
Success={() => <AsyncHandler.Success>...</AsyncHandler.Success>}
Error={() => <AsyncHandler.Error>...</AsyncHandler.Error>}>
</AsyncHandler>
This is most useful for components written to take advantage of this plugin, in the example above the AsyncHandler
component could look like this:
export default function AsyncHandler({ status, Loading, Success, Error }) {
switch (status) {
case 'success':
return <Success />;
case 'error':
return Error ? <Error /> : null;
case 'loading':
default:
return <Loading />;
}
}
AsyncHandler.Loading = ({ children }) => <>{children}</>;
AsyncHandler.Success = ({ children }) => <>{children}</>;
AsyncHandler.Error = ({ children }) => <>{children}</>;
Install babel-plugin-macros
and babel-plugin-jsx-on-demand-children
to your devDependencies
:
npm install --save-dev babel-plugin-macros babel-plugin-jsx-on-demand-children
Add babel-plugin-macros
to your babel configuration:
Adding babel-plugin-macros to your config
If installed as a macro, import it
import JsxOnDemandChildren from 'babel-plugin-jsx-on-demand-children/macro'
// or
const JsxOnDemandChildren = require('babel-plugin-jsx-on-demand-children/macro')
Create your custom component, ie:
export default function Conditional({ condition, If, Else}) {
return condition ? (<If />) : (<Else />);
}
Conditional.If = function({children}) {
return <>{children}</>;
}
Conditional.Else = function({children}) {
return <>{children}</>;
}
Use with <JsxOnDemandChildren>
:
import Conditional from './Conditional';
function SomeComponent() {
return (
<Conditional condition={isOk}>
<JsxOnDemandChildren>
<Conditional.If>
Everything is fine!
</Conditional.If>
<Conditional.Else>
We have a problem!
</Conditional.Else>
</JsxOnDemandChildren>
</Conditional>
);
}
MIT