React: Add a `React.Children.text` helper

Created on 24 Mar 2017  Â·  6Comments  Â·  Source: facebook/react

I've created a component roughly like this, serving to wrap the behavior of the SVG text element to add more sophisticated handling of the string. You can imagine something similar for generating simple a tags or spans or defn or whatnot…

export let MultilineText = ({children:text, ...props}) =>
  <text {...props}>
    {text.split(' ').map((str,i) =>
      <tspan key={i} dy="1em">{str}</tspan>
    )}
  </text>

The key point is that I've taken the children prop and assigned it to text locally, which makes the assumption that it will be a string that I can split!

However the React.Children docs discuss "dealing with the this.props.children opaque data structure" (emphasis mine). So technically my code's assumption could break in the future?

I would propose a React.Children.text helper that would guarantee stable behavior to this code:

export let MultilineText = ({children, ...props}) =>
  <text {...props}>
    {React.Children.text(children).split(' ').map((str,i) =>
      <tspan key={i} dy="1em">{str}</tspan>
    )}
  </text>

Specifically:

  • if "children" represents a single text node, return its string value
  • otherwise throw an assertion error

This would behave very much like the React.children.onlyChild logic and its invariant check, but expecting typeof children === 'string' rather than a ReactElement.isValidElement(children). (See also https://github.com/facebook/react/issues/1104.)

Most helpful comment

I needed something like this for a Button component that I was writing. It accepts icons as children, but I didn't want to make consumers explicitly provide a label prop. Here's the solution I used:

/**
 * The `BaseButton` component accepts any number of children. This flexibility
 * is used to support easily adding icons as children. However, we only want
 * to include strings when building accessible labels. Otherwise, it would say
 * [object Object] in the label.
 */
const getLabelFromChildren = (children) => {
    let label = '';

    React.Children.map(children, (child) => {
        if (typeof child === 'string') {
            label += child;
        }
    });

    return label;
};

All 6 comments

owever the React.Children docs discuss "dealing with the this.props.children opaque data structure" (emphasis mine). So technically my code's assumption could break in the future?

cc @bvaughn @gaearon do you guys think there is any chance of this in the future?

Why not get text through a property instead of children? It seems like the only reason you're getting text through children would be because you like how it looks, but that's never a good idea... or perhaps I'm missing something?

On another note, you could easily write your own implementation React.Children.text using the standard helpers, if you want to do it that way and stay future compatible for sure.

I think we need to clarify how opaque children really need to be. If there's a chance that React will ever use a different data structure to represent string children (instead of just the string itself) then it makes sense to add a helper. But I don't think that's likely, maybe @gaearon can chime in.

I don't think it's likely.

I needed something like this for a Button component that I was writing. It accepts icons as children, but I didn't want to make consumers explicitly provide a label prop. Here's the solution I used:

/**
 * The `BaseButton` component accepts any number of children. This flexibility
 * is used to support easily adding icons as children. However, we only want
 * to include strings when building accessible labels. Otherwise, it would say
 * [object Object] in the label.
 */
const getLabelFromChildren = (children) => {
    let label = '';

    React.Children.map(children, (child) => {
        if (typeof child === 'string') {
            label += child;
        }
    });

    return label;
};

In case anyone else ends up here, I discovered this package today and so far it works great!

https://github.com/fernandopasik/react-children-utilities

import Children from 'react-children-utilities'


const MyComponent = ({ children }) => Children.onlyText(children)

onlyText is recursive, and children can be any React Node. Awesome!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

UnbearableBear picture UnbearableBear  Â·  3Comments

trusktr picture trusktr  Â·  3Comments

bloodyowl picture bloodyowl  Â·  3Comments

zpao picture zpao  Â·  3Comments

Prinzhorn picture Prinzhorn  Â·  3Comments