47

I am currently in the process of designing the interface for a game engine that is written in JavaScript and ReactJS.

If a game object has a texture, the source of the texture will show up on the game object. For that to work, a game object needs to have the reference of the texture or more specifically the source of a texture. What I'm hoping for is to have a JSX snippet like the following one.

<GameObject>
  <Texture source="myimage.png" />
</GameObject>

The best solution I could come up with to get the reference is to have the texture as a prop, like so:

<GameObject texture={<Texture source="myimage.png" />} />

If the game engine-specific terminology is a bit too bewildering, think of it as a caption component inside a button component, where the caption component has specific data the button needs to access.

My question boils down to this: Is it possible to access children's prop after the children have been mounted without hacks or being an anti-pattern?

Johannes Stein
  • 1,951
  • 4
  • 18
  • 13
  • 2
    Have a look at the react-router code for how it parses children: https://github.com/rackt/react-router/blob/master/modules/createRoutesFromReactChildren.js – WiredPrairie Apr 16 '15 at 10:51
  • 3
    The function referenced is now in module [RouteUtils](https://github.com/rackt/react-router/blob/master/modules/RouteUtils.js) – blackwood Dec 09 '15 at 19:15
  • 3
    I expect it would have helped, but as the links are broken now, the information is lost. – ms007 Apr 05 '17 at 21:51
  • Likewise - @WiredPrairie could you please link to the canonical URL for git repos (i.e. with a commit hash in there so it doesn't break)? – GTF Mar 14 '18 at 17:10
  • Please provide at least summary in your answers, not just links. Links should only provide additional details. All links here are now broken. – Darko Maksimovic Mar 02 '19 at 15:51
  • 1
    Here is the full URL https://github.com/remix-run/react-router/blob/v5.3.4/packages/react-router/modules/Switch.js – Lonli-Lokli Feb 03 '23 at 18:12

5 Answers5

44

Because the links in the comments are broken, I enclose a link to the current react-router Switch module implementation where children are parsed (see the render() function). Also in this tutorial video the author accesses children props from parent.

To not bump into the same problem with link expiring again, here is how the code for accessing children props from parent would look like for above example when inspired by react-router's Switch:

(inside GameObject)

const { children } = this.props

React.Children.forEach(children, element => {
  if (!React.isValidElement(element)) return

  const { source } = element.props

  //do something with source..
})
Matej P.
  • 5,303
  • 1
  • 28
  • 44
  • 1
    Canonical URLs would prevent the links being broken as easily (obviously always best to post code directly in SO) – GTF Mar 14 '18 at 17:11
  • Here is the full URL https://github.com/remix-run/react-router/blob/v5.3.4/packages/react-router/modules/Switch.js – Lonli-Lokli Feb 03 '23 at 18:12
3

You should be able to just do this.props.texture.props. I'm not ware of it being an anti-pattern, but I don't think it's a common pattern either. It certainly looks like it may be breaking encapsulation if you want to access a specific prop.

You don't have to pass the element as prop to get a reference to it. You can access children via this.props.children.

See the documentation for more info.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 1
    This would definitely seem an anti-pattern to do. If there is something from the child you need in the parent maybe then use `state` in the parent and pass it to child as `prop` – Hozefa Nov 11 '16 at 00:24
  • 1
    I don't think this is an anti-pattern to react itself. However it is indeed an anti-pattern with the popuplar dataflow patterns used with react, such as Redux and Flux. It breaks the unidirectional flow of data. – V Maharajh Jan 10 '18 at 16:54
1

Is this the sort of thing you are thinking?

const Game = ({
  children
}) => {
  // small hack to turn single child, into array
  if (!children.length) {
    children = [children];
  }

  children.map((child, i) => {
    // now have access to props.source in parent
    console.log(child.props.source);
  })

  return ( < div > {
    children
  } < /div>
  );  
}

const Texture = ({source}) => {
  return (
    <div>Texture: {source}</div > );
}

ReactDOM.render(( < Game >
      < Texture source = "thing.png" / >
      < Texture source = "other.png" / >
      < /Game>
), document.getElementById('game'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='game'></div>

It's a little messy really.

I would personally either give Game an array of textures to load itself.

Or decouple Game and Texture entirely, so data flows only one way.

Phil Poore
  • 2,086
  • 19
  • 30
0

What needs to own the texture, and what should its parent be?

Could you define a GameObject that owns and is the parent of the Texture? eg:

<GameObject textureSoure='myimage.png' />

And GameObject then renders the texture (In GameObject's render() )?

return <....>
   <Texture source={ this.props.textureSource } />
     </....>
WayneC
  • 5,569
  • 2
  • 32
  • 43
0

Another approach to the same problem can be:

Add the date that has to be accessed from the children conditionally to the parents prop as well...

You can never go wrong with this

JJY9
  • 100
  • 9