Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to deep traverse React Children without rendering?

Is there any way to grab all of the bar properties in <Wrapper/> below 'statically', e.g. without rendering?

import React from 'react';
import ReactDOM from 'react-dom';

class Foo extends React.Component {
  render() {
    return(
      <div>
        <span bar="1" /> // want to collect this 'bar'
        <span bar="2" /> // want to collect this 'bar'
      </div>;
    );
  }
}


class FooTuple extends React.Component {
  render() {
    return(
      <div>
        <Foo />
        <Foo />
      </div>;
    );
  }
}

class Wrapper extends React.Component {
  render() {

    React.Children.forEach(this.props.children, child => {
      console.log(child.props); // can only see <FooTuple/> not <Foo/>
    });

    return(
      <div>
        {this.props.children}
      </div>;
    );
  }
}

ReactDOM.render(
  <Wrapper>
    <FooTuple />
  </Wrapper>, 
document.getElementById('app'));

Here's a webpackbin with a naive attempt that tries to iterate over child.children which obviously doesn't work, but it's here if it's helpful: http://www.webpackbin.com/EySeQ-ihg

like image 984
Muers Avatar asked Mar 11 '16 01:03

Muers


People also ask

Is it possible to use React without rendering HTML?

Yes it is very much possible and very much useful, in case of lazy loading components.

Does React always render kids?

In normal rendering, React does not care whether “props changed” - it will render child components unconditionally just because the parent rendered!

Does every React component need a render?

Taking advantage of the fact that components don't have to render anything has made my components much more modular and compliant with the Single Responsibility Principle. As with any other code, its best when React components have only one major purpose.

Which function is used in rendering a component with no children?

For class components, the prop is called in their render function. For functional components, the prop is called in the component function. We can use any prop for the function, not just children , so using the word “children” can be inaccurate.


1 Answers

TL;DR; Nope that's not possible.

--

I've once encountered the same problem trying to traverse a tree of deeply nested children. Here are my scoop outs:

Required knowledge

  • children are what's placed inside the jsx open and close tags, or injected directly in the children prop. other than that children prop would be undefined.

    <div className="wrapper">
      // Children
      <img src="url" />
    </div>
    
    /* OR */
    
    <div classname="wrapper" children={<img src="url" />}>
    
  • children are an opaque tree-like data structure that represents the react elements' tree, it's likely the output of React.createElement that the jsx implements when transpiling.

    {
      $$typeof: Symbol(react.element),
      type: 'div',
      key: null,
      ref: null,
      props: {
        className: "wrapper",
        children: {
          $$typeof: Symbol(react.element),
          type: 'img',
          key: null,
          ref: null,
          props: { src: 'url' },
        }
      }
    }
    
  • Creating React elements doesn't mean that they are instantiated, think of them like a descriptor that React uses to render those elements. in other words, instances are taken care off by React itself behind the scenes.

Traversing children

Let's take your example and try to traverse the whole tree.

<Wrapper>
  <FooTuple />
</Wrapper>

The opaque children object of these elements would be something like this:

{
  $$typeof: Symbol(react.element),
  type: Wrapper,
  key: null,
  ref: null,
  props: {
    children: {
      $$typeof: Symbol(react.element),
      type: FooTuple,
      key: null,
      ref: null,
      props: {},
    }
  }
}

As you can see FooTuple props are empty for the reason you should know by now. The only way to reach it's child elements is to instantiate the element using it's type to be able to call it's render method to grab it's underlying child elements, something like this:

class Wrapper extends React.Component {
  render() {
    React.Children.forEach(this.props.children, child => {
      const nestedChildren = new child.type(child.props).render();

      console.log(nestedChildren); // `FooTuple` children
    });

    return(
      <div>
        {this.props.children}
      </div>;
    );
  }
}

This is obviously not something to consider at all.

Conclusion

There is no clean way to augment deeply nested children or grab something from them (like your case). Refactor your code to do that in a different manner. Maybe provide a setter function in the context to set the data you need from any deep child.

like image 70
Mohammed Erraysy Avatar answered Oct 18 '22 03:10

Mohammed Erraysy