Skip to content

ulyssesrr/babel-plugin-jsx-on-demand-children

Repository files navigation

babel-plugin-jsx-on-demand-children

Babel plugin/macro offering a pleasant syntax for rendering children on demand.


Build Status Code Coverage version MIT License

Babel Macro PRs Welcome Code of Conduct

Introduction

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}</>;

Installation

Option 1: As a macro (Recommended)

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

Usage

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>
  );
}

Sandbox Demos

LICENSE

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published