react-grouped-children
v0.1.4
Published
Enables creation of grouped component inheritance hierarchies for React components
Downloads
291
Readme
react-grouped-children
A React library to allow passing multiple children groups to a component using classic React component inheritance hierarchies instead of attributes.
Table of Contents
Installation
npm install react-grouped-children
or
yarn add react-grouped-children
Usage
Defining Children Specification
Create a childrenSpec
object where each key is a grouping component name and its value is the component itself.
If you don't have a component for a particular group, you can set its value to null
. If value is not set a proxy
component will be generated and its children passed to primary component props, not the proxy component.
const childrenSpec = {
Header: Header,
Footer: null,
} as const;
Modifying Component
Wrap your component (main component) with withGroupedChildren
and pass childrenSpec
as the second argument
to define grouping components.
import { withGroupedChildren } from 'react-grouped-children';
const MyComponentInternal: React.FC<MyComponentProps & GroupedChildrenProps<typeof childrenSpec>> = ({
header,
footer,
...
}) => ...
const MyComponent = withGroupedChildren({ childrenSpec })(MyComponentInternal);
With childrenSpec
defined as in the example above header
prop will contain an array with all instances of Header
component and footer
prop will be set to an array of children of the generated proxy component.
⚠ NOTE: be mindful that react may pass children as single child or as an array, so
footer
may contain array of arrays. To properly control this you can define traverseChildren function in config or do proper parsing in the component. This project does not address it as it will be fully compatible withReact.ReactNode
type and React can handle it properly if you pass it as is when you rendering the main component.
⚠ NOTE: You may have some challenges with
key
property if you pass elements to render return "as-is". Most like this will happen only if your customchildrenToArray
function does not assign keys properly.
Using Children Groups and HOC
Now, you can pass children group components to your component as follows:
...
return (
<MyComponent>
<MyComponent.Header>
This is header
</MyComponent.Header>
<MyComponent.Footer>
This is footer 1
</MyComponent.Footer>
<MyComponent.Footer>
This is footer 2
</MyComponent.Footer>
</MyComponent>
)
...
Configuration
You can configure the behavior of withGroupedChildren by passing an optional config object.
Mode details are in JSDoc comments of Config
type (./src/types.ts
)
childrenSpec
Specification which defines children components. Should represent object with keys in Pascal case to define
groping component name. Value can be set to an implementation of grouping component or null
to use default
component generated by default factory.
childrenToArray
A custom method to convert React component initial children to array on preprocessing stage.
Use when you want to flatten children. The function must always returned a cloned array of children
as it will be mutated. If not defined, React.Children.toArray
is used by default.
getComponentName
A custom HOC name generation factory. Returns custom component name.
proxyComponentFactory
A factory which accepts current key
of specification object returns a custom implementation of Proxy component.
⚠ Be mindful that this factory should return a new component every for every separate key. This is because the default
componentMatcher
is matching types by reference, not by display name or anything else. To change this define your owncomponentMatcher
traverseChildren
A custom method to traverse children from Proxy Component. This method is generic, return type should match with the
second generic (T
) parameter of GroupedChildrenProps
componentMatcher
A custom component matcher
Limitations
Static Properties/Methods
To enable grouping functionality withGroupedChildren
adds grouping components as static properties to resulting HOC.
You need to ensure that you copy them over if you use other HOC on top. More details here
Why?
in classic React when you want to divide children into groups to use them separately in different
sections of your app you need to pass them as React.ReactNode
attributes which often make code reading
challenging, especially when you need to have many attributes like this:
<MyComponent
group1={
<div>
<Head>Some content</Head>
<Body>
Some bigger content
<ul>
{elements.map((e) => <li key={e}>{e}</li>)}
</ul>
</Body>
<div>
}
group2={
<div>
<Footer>
<FooterItem>Item 1</FooterItem>
<FooterItem>Item 2</FooterItem>
<FooterItem>Item 3</FooterItem>
</Footer>
<div>
}
>
{restChildren}
</MyComponent>
With this package it becomes more transparent and look more HTML-ish:
<MyComponent>
<MyComponent.Group1>
<div>
<Head>Some content</Head>
<Body>
Some bigger content
<ul>
{elements.map((e) => <li key={e}>{e}</li>)}
</ul>
</Body>
<div>
</MyComponent.Group1>
<MyComponent.Group2>
<div>
<Footer>
<FooterItem>Item 1</FooterItem>
<FooterItem>Item 2</FooterItem>
<FooterItem>Item 3</FooterItem>
</Footer>
<div>
<MyComponent.Group2>
{restChildren}
</MyComponent>
or in scenarios like this
<MyList>
<MyList.Item>Item 1<MyList.Item>
<MyList.Item>Item 2<MyList.Item>
<MyList.Item>Item 3<MyList.Item>
<MyList.Item>Item 4<MyList.Item>
<MyList.Item>Item 5<MyList.Item>
<MyList.Heading>Heading Line</MyList.Heading>
<MyList.Footer>Heading Line</MyList.Footer>
</MyList>
and access children as classic attributes:
const MyList: React.FC = ({ item, heading, footer }) => {
...
return (<div>
{heading}<br />
<ul>
{item.map((i) => <li>{i}</li>)}
</ul>
{footer}
</div>)
}
License
Copyright (c) 2023 Alexandr Yeskov