9

Given this object:

lst socials = {
  foo: 'http://foo'
}

I want to loop through it in JSX. This works:

let socialLinks = []
let socialBar
for (let social in socials) {
  socialLinks.push(<li>
                     <a alt={social} href={socials[social]}>{ social }</a>
                   </li>)
}
if (socialLinks) {
  socialBar = <div className='align-bottom text-center'>
                <ul className='list-inline social-list mb24'>
                  {socialLinks}
                </ul>
              </div>
}

But this doesn't (social undefined):

let socialBar
if (socials) {
  socialBar = <div className='align-bottom text-center'>
                <ul className='list-inline social-list mb24'>
                  for(let social in socials)
                  {<li>
                     <a alt={social} href={socials[social]}>{ social }</a> // social is undefined
                   </li>}
                </ul>
              </div>
}

What is the reason social is undefined in the 2nd example? I assume there is a scoping issue with the inner brackets but I have not been successful fixing it.

I can do do a forEach with object keys and do as in this post but that's not much different than my working example.

To be clear - I have it working, I simply wish to be clearer on the scoping problem (or syntax error if so) in my 2nd example.

Community
  • 1
  • 1
cyberwombat
  • 38,105
  • 35
  • 175
  • 251
  • 3
    Surround your `for` loop with `{}` and try. Although it might work, usually what is done is use the `.map()` array method in order to achieve the same result. – Diego Cardoso Oct 04 '16 at 03:08
  • @DiegoCardoso - no go. That yields a syntax error when I try. Which is odd as I thought things in bracket could be any JS but it doesn't like a for loop. Using a `map` in the same brackets works. – cyberwombat Oct 04 '16 at 04:37

2 Answers2

4

JSX is just sugar that gets transpiled to a bunch of function calls of React.createElement, which you can find the docs for here: https://facebook.github.io/react/docs/top-level-api.html#react.createelement

ReactElement createElement(
  string/ReactClass type,
  [object props],
  [children ...]
)

Basically your JSX goes from

<div style="color: white;">
  <div></div>
</div>

to

React.createElement('div', { style: { color: 'white' } }, [
  React.createElement('div', {}, [])
])

For the same reason you can't pass a loop to a parameter in a function, you can't put a loop into JSX. It would end up looking like

React.createElement('div', { style: { color: 'white' } }, [
  React.createElement('div', {}, for (;;) <div></div>)
])

which doesn't make sense at all because you can't pass a for loop as a param. On the other hand, a map call returns an array, which is the correct type for the third parameter of React.createElement.

React is still a virtual dom library at the end of the day, but JSX just makes it more familiar to write. hyperscript is another good example of a vdom library, but where JSX is not standard. Their example on their README is similar to what React would look like without JSX:

var h = require('hyperscript')
h('div#page',
  h('div#header',
    h('h1.classy', 'h', { style: {'background-color': '#22f'} })),
  h('div#menu', { style: {'background-color': '#2f2'} },
    h('ul',
      h('li', 'one'),
      h('li', 'two'),
      h('li', 'three'))),
    h('h2', 'content title',  { style: {'background-color': '#f22'} }),
    h('p',
      "so it's just like a templating engine,\n",
      "but easy to use inline with javascript\n"),
    h('p',
      "the intension is for this to be used to create\n",
      "reusable, interactive html widgets. "))
m0meni
  • 16,006
  • 16
  • 82
  • 141
3

In your JSX you can't have a for loop. So even if you have {} around your for loop it doesn't work. Instead use a map as shown in the below code. Assuming your data socials is an array and not just an object.

If socials is an object you need to use Object.keys(socials).map(function(key)){}

class App extends React.Component {
  render() {
    let socialBar = null;
    let socials = [{
  foo: 'http://foo'
}]
if (socials) {
  socialBar = <div className='align-bottom text-center'>
                <ul className='list-inline social-list mb24'>
                  {socials.map(function(social, index) {
                     
                     return <li key={index}>
                     <a alt={index} href={social.foo}>{ social.foo }</a> 
                   </li>
                    
                  }) }
                </ul>
              </div>
}
    return (
      <div>{socialBar}</div>
    )
  }


}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.js"></script>
<div id="app"></div>
cyberwombat
  • 38,105
  • 35
  • 175
  • 251
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • 1
    Are there any docs on this? I can see that it doesn't work and that a `map` does - as I said I have it working so I am not so much looking for another solution as a deeper understanding of the jsx/scope syntax. – cyberwombat Oct 04 '16 at 04:39
  • @Yashua JSX is just sugar that gets transpiled to a bunch of function calls. It becomes something like `React.createElement(x, y, [React.createElement(x, y, [])])`. For the same reason you can't pass a loop to a parameter in a function, you can't put a loop into JSX. React is like hyperscript https://github.com/dominictarr/hyperscript in this way because it's a virtual dom library. A map call returns an array, which is the third parameter of `React.createElement`. A for loop as a param is just broken syntax like `console.log(for (;;) console.log(hi))` would be. – m0meni Oct 04 '16 at 04:42
  • @AR7 thank you that is the answer I was looking for. Please post as an answer. – cyberwombat Oct 04 '16 at 04:48