Passing props to React components: when to use {}

One of React challenges had me to pass a number 10 to my component and I had to wrap it in {} (Not using {} would give wrong result).

But I realized you can pass string values without {}.

Why is that if you send a string value without {} React treats it as JavaScript string value but if you send 10 not wrapped in {} It won’t be treated as JavaScript number value by React?

It’s a model of HTML, so it works like HTML in that way. It’s convenient, otherwise you’d have to write attribute={ "value" } every time you wanted to use a string.

That has to be the case, otherwise you couldn’t use strings, consider

<User
  name="Jack"
  age="99"
  phonenumber="+441234567890"
  id="0b0101"
/>
  • age is a number, so that would be 99.
  • phonenumber isn’t a number, if it’s parsed as a number I’d get 441234567890, which is wrong.
  • id isn’t a number, if it’s parsed as a number I’d get 5, which, again, is wrong.

There has to be a way to differentiate strings and numbers.

can you be more specific of the code, give example of particular case

Thank you. But to differentiate strings and numbers isn’t it enough to use quotation marks instead of {}?!

<User
  age=99
  id="0b0101"
/>

The prop age's value is not wrapped in quotations so it’s a number as it should be and the prop id's value is wrapped in quotations so it’s a string and should no be converted to a number.

Let’s see if my conception of JSX syntax is correct.
Every where in your component code you can write JavaScript code without wrapping them in {} but as you’re combining HTML and JavaScript or writing JSX code you need to explicitly specify your JavaScript code inside {}. like:

<User age={99}/>

But there’s an exception which is for string values and it’s that string values don’t need {} necessarily .

Sort of.

JSX isn’t HTML, you aren’t mixing HTML in. It looks like HTML, that’s the point, but it isn’t. It’s a form of XML embedded in JS (JavaScript XML, JSX).

JSX is a convenient visual aid, that’s all. It’s not necessary with React, just most people find it makes things easier. It is important to stress that it is not valid JS. Therefore you need a tool to convert it to JS (eg Typescript, or Babel).

The syntax has to have a set of simple rules to make sure it’s easy to convert. I’ll describe them below, but just to say now before I go through this, there are six basic tokens: <, >, {, }, and " or' (can use either kind of quotation, double or single, to match JS). < and > are used to denote containers for JSX elements. { and } are used to denote JS expressions (something that evaluates to a value). " or ' are used to denote strings. Strings are dead easy [he says…]. Expressions, well, they can be anything, so you need something completely unambiguous.

The problem with this:

<User name="Jack" age=99 />

Is that it’s highly ambiguous. It could be allowed. There now needs to be another set of rules put in place especially for values that are attributes that are not strings. So that, a number, looks like it can work. But the { are used anywhere there’s an expression, it’s nice and simple, all works the same way. So here, doing exactly the same thing:

const User = ({ name, age }) => <p>{ name } is { age }</p>

But those curly brackets can’t be removed – <p>name is age</p> will generate literally that, "name is age", so they are required there. So now instead of having the one rule, there’s an extra rule specifically for attributes. This really starts to break down outside of simple values like 99. Even pretty basic things, I dunno, here’s some random stuff:

<Example a=4 + 2 b=3 - 1 />
<Example c=typeof someVariable />
<Example d=new Date() />

These are just unparesable. So could say “you can miss off the curly braces if it’s not something basic, but if it’s something with, say, spaces in, you need them”. What about this:

<Example e={ key: value } />

It’s just not going to work at all well. In exchange for saving someone typing { and }, there are now a huge amount of difficult to solve problems.

So, anyway, here is an explanation of what JSX syntax is doing.

!!! NOTE !!! the following is extremely long. Apologies for the length, I’ve just tried to go step by step, reiterating things over and over, in the hope it might cement some concepts. It involved quite a lot of copy pasting and I was writing it very quickly, so apologies in advance for any minor errors. NOTE It makes no attempt to explain how React handles the resulting code generated from the JSX.


So as an example, this

const User = ({ name }) => <p>{ name }<p>;

const Jack = () => <User name="Jack" />

Converted (not by React, but by the tool being used) to some actual JS, it ends up looking [basically] like:

const User = ({ name }) => jsx("p", { children: name });

const Jack = () => jsx(User, { name: "Jack" });

Note that:

  1. the Babel playground shows you the transformation
  2. I’m using the newer transform available in React; the earlier version transforms to a function like React.createElement(<element>, <attributes>, <children>) rather than jsx(<element>, <attributes>). Does the same thing overall though.

So what the JSX actually is, once it’s been converted to actual JS, is a function called jsx that takes two parameters; the element + an object with the properties of that element (attributes and children).

Children can be strings, so are rendered as text. Or they can be other components, which also get converted to jsx functions.

And a component is just a function that in turn calls that jsx function.

When the User function is ran, like User({ name: "Jack" }), that then runs jsx("p", { children: "Jack" }).

That jsx produces a tree of objects. And running the functions modifies that tree.


There are a set of rules regarding JSX to allow it to be converted in a consistent manner. So, to simplify slightly (it isn’t much more complex than this, but it has to handle a lot more situations than this very simple one):

  • Every JSX element (what gets converted to the jsx function) is identified with an opening <, followed by some other characters, followed by:
    1. A closing >: there is a closing element (eg <Users></Users>).
    2. A closing />: element is self-closing (like <User />).
  • The characters immediately after < (opening element) and up until the first space character (or the closing >) are the element name. That gives you the first argument passed to jsx.
  • After that first space character that comes after the name, the opening element can have as many attributes as you want, with the syntax attributeName="attributeValue". These go into the second argument passed to jsx as { attributeName: "attributeValue" }
  • These are, as you’ve noticed, strings.
  • So along with < and >, The other important characters in JSX are { and }. These indicate a JavaScript expression. Opening {, followed by the expression, followed by the closing }. An expression is a piece of code that evaluates to a value. So "hello", true, 1, "cat".toUppercase(), myFunction(), [1, 2, 3].map(v => v * 2): these are all expressions*.
  • If they are an attribute value – like <User age={99} /> – then this means that the correct thing goes into the jsx function’s second argument: jsx(User, { age: 99 }). It’s unambiguous. When a { is reached, this is going to be something that evaluates a value. Grab everything up until the closing }, evaluate it, then that value goes into the function.
  • Characters after > and before <, unless there’s another < or {, are assumed to be text, they will go into the second argument under the key children. The rules don’t change: if a { is encountered, it’s a JS expression. Evaluate everything up until the closing }. If it’s a <, it’s an element, so start the process again.

So (and I’m simplifying, but as I say it’s not hugely more complicated than this):

<p>Hello</p>
  • Opening < not followed by /
    That gives us jsx
  • The character immediately after that < is the string "p". Nothing else up to closing >. So the element name is "p".
    That gives us jsx("p")
  • The element has no attributes.
    Still jsx("p")
  • Now some characters. Everything until< is a string.
    Now jsx("p", { children: "Hello" })
  • Opening </. It’s a closing element. Check up to > – can see it’s the string "p". This matches the opening element, so it’s the matching closing element. Finish.

So for something more complex:

const Users = ({ children }) => <ul>{ children }</ul>;
const User = ({ name, age }) => <li>{ name } is { age } years old</li>;

const App = () => (
  <Users>
    <User name="Jack" age={ 99 } />
    <User name="Jill" age={ 102 } />
  </Users>
);

const myApp = <App />;

For Users: it’s a function with a single defined property in the props parameter: children (this is actually a special prop automatically provided). So Users is basically a function that takes a single argument, children.

Opening <, it’s an element, followed by the element name, "ul", and then >. Not self-closing, so we may have some child elements and there’ll need to be a closing </ul>. We go past > and immediately get { – there’s a JS expression to be evaluated. That is children, the argument passed into the function, so whatever they are gets evaluated when the function runs. Then a closing_ </. Does the name match a previous, currently unmatched element? Yes, it’s "ul", so close everything off.

Note that the element name is a string – "ul". The processor will use that to look up and generate another jsx function from a predefined list of names, for convenience so that I don’t need to define what ul is, but just bear in mind it’s doing the exact same thing as the Users function I’ve defined here itself.

const Users = ({ children }) => jsx("ul", { children });

For User: it’s basically a function with two parameters: name and age. Opening <, it’s an element, followed by the element name, li. No attributes, so go past > and immediately get { – expression. This is name, so whatever that is. Then closing }. Now there’s some text, " is ". Then another expression, evaluate age. Then some more text, " years old". Then a closing_ </. Does the name match a previous, currently unmatched element? Yes, it’s "li", so close everything off.

What’s between <li> and </li>, that goes expression, text, expression, text. The expressions are variable (they depend on what’s passed into the function), but the text is static; they get split into a list of children.

const User = ({ name, age }) => jsx("li", { children: [name, " is ", age, " years old"]});

Evaluate the App function. Opening <, it’s an element, followed by the element name, Users, and then >. Not self-closing, so we may have some child elements and there’ll need to be a closing </Users>.

const App = () => jsx(Users, { children: ............

Notice that the first argument isn’t a string, it’s a variable. Which refers to the Users function, so evaluate that. This refers to the User function, twice, so evaluate that, twice.

Anyway, opening <, sure enough, it’s an element, followed by the element name, User, followed by an attribute name, name with a string value "Jack", followed by an attribute name, age, followed by an opening{. There is a JS expression, so evaluate up until }: that produces the value 99. Then a self-closing />. One child.

const App = () => jsx(Users, { children: [ jsx(User, { name: "Jack", age: 99 }) ] });

Opening <, followed by the element name, User, followed by an attribute name, name with a string value "Jill", followed by an attribute name, age, followed by an opening{. There is another JS expression, so evaluate up until }: that produces the value 102. Then a self-closing />. Two children.

const App = () => jsx(Users, { children: [ jsx(User, { name: "Jack", age: 99 }), jsx(User, { name: "Jill", age: 102 }) ] });

Closing </. Does the name match a previous, currently unmatched element? Yes, it’s Users, so close everything off.

Finally, evaluate myApp, so <App />: opening <, it’s an element, followed by element name, "App", and /> means self-closing.

const myApp = () =>  jsx(App, {});

We’ve got the App function evaluated above, so App calls Users, and Users calls User (kinda, it’s a bit better managed than how I describe so that React doesn’t run absolutely everything in the entire app every single time anything changes).


* This is why you can’t use for loops in JSX: they are statements, not expressions, they don’t evaluate to a value.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.