Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to use render props in React to expose information about a component's state

I have read about render props extensively on the official React documentation as well as other articles. However, I am trying to do something similar to what Tailwind does and am failing to figure out how they use this pattern in their components to expose state information about a component.

For example, if you have a look at their Switch component. Its usage is as follows:

function MyToggle() {
  const [enabled, setEnabled] = useState(false)

  return (
    <Switch checked={enabled} onChange={setEnabled} as={Fragment}>
      {({ checked }) => (
        /* Use the `checked` state to conditionally style the button. */
        <button
          className={`${
            checked ? 'bg-blue-600' : 'bg-gray-200'
          } relative inline-flex h-6 w-11 items-center rounded-full`}
        >
          <span className="sr-only">Enable notifications</span>
          <span
            className={`${
              checked ? 'translate-x-6' : 'translate-x-1'
            } inline-block h-4 w-4 transform rounded-full bg-white transition`}
          />
        </button>
      )}
    </Switch>
  )
}

If I were to write the Switch component from scratch, I would instantiate a state for checked in the component function but how do I make it so that the state is exposed when I use the component in the manner:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

<Switch>
      {({ checked }) => (
        ...
      )}
</Switch>

From their github,they do something like:

function SwitchFn(props) {
  let {
    checked,
    ...theirProps
  } = props

  let [checked, onChange] = useControllable(controlledChecked, controlledOnChange, defaultChecked)

  let slot = useMemo<SwitchRenderPropArg>(() => ({ checked }), [checked])
  ...
return (
    <>
      {name != null && checked && (
        <Hidden
          features={HiddenFeatures.Hidden}
          {...compact({
            as: 'input',
            type: 'checkbox',
            hidden: true,
            readOnly: true,
            form,
            checked,
            name,
            value,
          })}
        />
      )}
      {render({ ourProps, theirProps, slot, defaultTag: DEFAULT_SWITCH_TAG, name: 'Switch' })}
    </>
  )
}

But it is quite difficult to follow along. Please help. Thank you.

>Solution :

To be able to do

<Switch>
      {({ checked }) => (
        ...
      )}
</Switch>

It means that children is a callback instead of a React Node.

In the component implementation it means that instead of simply calling children that evaluates to a Node, you would call children(...).
So you could implement Switch with something like

function Switch({children}){
  const [checked, setChecked] = useState(false);
  ...

  return (
    // it's of course more complex that that, but the core idea is here
    <div onClick={() => setChecked(current => !current)}> 
     {children({checked})}
    </div>
  );

}

This is quite similar to having a render prop, except that the prop is in case is actually children.

Note

You could ask "But where is children actually called in that Tailwind code???"

switch.tsx uses utils/render.ts which extracts children from the props.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading