Skip to content

Instantly share code, notes, and snippets.

@granmoe
Last active December 7, 2022 14:50
Show Gist options
  • Save granmoe/274c299b792b039deecfb619753ea32c to your computer and use it in GitHub Desktop.
Save granmoe/274c299b792b039deecfb619753ea32c to your computer and use it in GitHub Desktop.
Ever wanted to join react children like you join an array?
This file is only here to provide the title of the gist
import React from 'react'
import joinChildren from 'react-join-children'
import { Item, Separator } from 'justanexample'
export default class MyComponent extends React.Component {
render () {
return <div>
{ joinChildren(this.props.items, this.renderItem, this.renderSeparator) }
</div>
// with 3 items, joinChildren returns [<Item>, <Separator>, <Item>, <Separator>, <Item>]
}
renderItem = (item, index) => {
return <Item key={ index } className="item" />
}
renderSeparator = key => {
return <Separator className="separator" key={ key } />
}
}
export default function (children, render, renderSeparator) {
return children.reduce((result, child, index) => {
if (index < children.length - 1) {
return result.concat([render(child, index), renderSeparator(index + '-separator')])
}
return result.concat(render(child, index))
}, [])
}
@fqueiruga
Copy link

Works perfectly, thanks!

@ivan-kleshnin
Copy link

ivan-kleshnin commented Sep 28, 2017

The better function here is flatMap (or chain in Ramda). Unfortunately JS does not provide one (yet).

<p className="conceps inline list">
  {flatMap((concept, i) =>
    [concept, <span key={i} className="separator">&#8226;</span>]
  , R.map(removeTags, lesson.concepts))}
</p>

generates something like

Function • Function type • Higher-order function • Partial application

P.S.

for Ramda it's R.addIndex(R.chain) instead of flatMap above.

@LinusU
Copy link

LinusU commented Oct 4, 2017

Shameless plug for a similar module I just published: react-with-separator

const WithSeparator = require('react-with-separator')
 
const React = require('react')
const { render } = require('react-dom')
 
const Footer = ({ username }) => (
  <WithSeparator separator=' | '>
    (!username && (
      <a href='/login'>Login</a>
    ))
 
    (username && (
      <a href='/logout'>Logout</a>
    ))
 
    <a href='/help'>Help</a>
  </WithSeparator>
)
 
render(<Footer username='linusu' \>)

could also be used as such, to match usage in first post:

export default class MyComponent extends React.Component {
  render () {
    return <WithSeparator separator={<Separator className="separator" />}>
      {this.props.items.map((item, index) => this.renderItem(item, index))}
    </WithSeparator >
    // with 3 items, joinChildren returns [<Item>, <Separator>, <Item>, <Separator>, <Item>]
  }
  
  renderItem = (item, index) => {
    return <Item key={ index } className="item" />
  }
}

@dinzy17
Copy link

dinzy17 commented May 3, 2018

Simple approach to this will be using map() :

import React from 'react'

export default class MyComponent extends React.Component {
  render () {
    return (
      <div>
        {
          map(props.items, (item, index) => {
            return (
              <span>
                { !!index && ', '}
               <Item key={ index } className="item" />
              </span>
            )
          })
        }
      </div>
    )
  }
}

@Gerst20051
Copy link

Thanks for the idea @dinzy17 that worked great!

@zachwolf
Copy link

Thanks :)

@granmoe
Copy link
Author

granmoe commented Jun 11, 2019

Thanks :)

Whoa...somehow I never got any notifications on this until yours, @zachwolf. Oh the mortifying feeling when you find out other human beings have looked at a random thing that you wrote two years ago :P My style has evolved since then, but I think the concept stands the test of time.

Anyhow...glad I could help?

@basith374
Copy link

been searching for this for an hour. thanks so much

had to find it using site:gist.github.com

@granmoe
Copy link
Author

granmoe commented Oct 2, 2020

So, some years have passed, and now we have React fragments, which don't add any extra nodes to the DOM, so we can simply do:

      {things.map((thing, index) => (
        <>
          {index > 0 && <Separator />}
          <Thing />
        </>
      ))}

This is similar to @dinzy17's approach above, but it doesn't add an extra wrapper around each item (just for cases where you don't or can't add an extra wrapper).

@j-hannes
Copy link

j-hannes commented Jun 24, 2021

@granmoe you need to assign a key prop to the fragments:

{list.map((item, index) => (
  <React.Fragment key={item.id}>
    {!!index && <Separator />}
    <Item item={item} />
  </React.Fragment>
))}

@granmoe
Copy link
Author

granmoe commented Jun 24, 2021

@granmoe you need to assign a key prop to the fragments:

{list.map((item, index) => (
  <React.Fragment key={item.id}>
    {!!index && <Separator />}
    <Item item={item} />
  </React.Fragment>
))}

Yep, good catch.

(This was just a hastily written example, but may as well not encourage poor practices 👍)

@benkeil
Copy link

benkeil commented Dec 7, 2022

import React, { FunctionComponent, ReactNode } from 'react';

export const Separated: FunctionComponent<{ separator?: ReactNode }> = ({ children, separator = ' ' }) => {
  return (
    <>
      {React.Children.toArray(children)
        .reduce<ReactNode[]>((previousValue, currentValue) => {
          return [...previousValue, currentValue, separator];
        }, [])
        .slice(0, -1)}
    </>
  );
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment