最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Is it possible to deep traverse React Children without rendering? - Stack Overflow

programmeradmin5浏览0评论

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:

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

Share Improve this question edited Mar 11, 2016 at 1:55 Muers asked Mar 11, 2016 at 1:50 MuersMuers 3,2403 gold badges28 silver badges32 bronze badges 4
  • 1 you can get to the level of the current render, not deeper. when you run your log, you are in the Wrapper.render(), so you can see only the stuff defined by whatever rendered that, which is the stuff in the last ReactDOM.render call, <FooTuple>. from FooTuple.render(), you could see <Foo/>, and from Foo.render, your <span>s. – dandavis Commented Mar 11, 2016 at 3:14
  • It's difficult to answer this question without knowing what the context or use case is. As noted they won't be accessible where you have the console.log() right now, but you're rendering it into the DOM, so the <span>s will be in the DOM. – JMM Commented Mar 11, 2016 at 14:39
  • 1 @JMM the use case is I'm building a Form that needs name properties (and one or two other properties) for arbitrarily deeply nested component children of the Form. I need to be able to grab them at first run/instantiation time for things like validation etc. I was thinking it should be possible to traverse the children of the form and decorate them, maybe using React.cloneElement, but I wasn't able to traverse deeper than the first-level children. – Muers Commented Mar 14, 2016 at 15:05
  • @Muers It's still not clear to me what you're trying to do or how the two things you mentioned relate to each other. You can pass props down arbitrarily deep, you can pass ReactClasses or ReactElements or callback functions as props, and if you really want to you could use context. – JMM Commented Mar 14, 2016 at 18:20
Add a comment  | 

1 Answer 1

Reset to default 19

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.

发布评论

评论列表(0)

  1. 暂无评论