JSX deep dive
@h6165
Abhishek Yadav
Co-organizer: Chennai.js
The agenda
JSX
- The syntax
- The idea/rationale
- Interpolation
- Custom elements, components
- JSX conditionals
- JSX loops
- JSX without React ?
JSX
// This should be counted as valid javascript
var el = <div>Hello World</div>;
The idea
So that -
=> We can write HTML syntax within Javascript
=> => That gets converted to equivalent real HTML when needed
JSX
// This should place the div within document
var el = <div>Hello World</div>;
ReactDOM.render(el, document)
The idea
JSX
<!-- A more sane example -->
<div id='root'></div>
<script type='text/babel'>
const rootElement = document.getElementById('root')
const el = <div>Hello World</div>;
ReactDOM.render(el, rootElement)
</script>
The idea
Create that Hello World element,
insert it inside the root element
JSX
<!-- Just like this ? Jquery FTW !! -->
<div id='root'></div>
<script type='text/babel'>
const el = $('<div>Hello World</div>')
$('#root').html(el)
</script>
The idea
JSX
<!-- Or even plain Javascript -->
<div id='root'></div>
<script type='text/babel'>
const rootElement = document.getElementById('root')
rootElement.innerHTML = '<div>Hello World</div>'
</script>
The idea
JSX
The idea
Then why JSX ?
It seems to achieve the same goals as the older techniques
JSX
The rationale
The goal -
- be a template (for React)
- simplify composition (components) (for React)
- no direct DOM manipulation (for React)
(The React vision, maybe)
JSX
As a template
<!-- A more sane example -->
<div id='root'></div>
<script type='text/babel'>
const rootElement = document.getElementById('root')
const name = 'Abhi'
const el = <div>Hello {name}</div>;
ReactDOM.render(el, rootElement)
</script>
JSX
As a template
- Templates (handlebars etc) are not always easy to compose.
- We often want to build larger UI using smaller template fragments (Components)
JSX
As a template - composition
// We need something like this
// (Incorrect syntax below)
const Hello = <div> Hello {name} </div>;
const Score = <div> Your score is {score} </div>
const el = <div>
<Hello />
<Score />
</div>
ReactDOM.render(el, rootElement)
JSX
As a template - composition
// The following will work
const Hello = () => <div> Hello {name} </div>
const Score = () => <div> Your score is {score} </div>
const el = <div>
<Hello />
<Score />
</div>
ReactDOM.render(el, rootElement)
A function with capitalized name becomes a React component
JSX
As a template - composition
// Even better
const Hello = (props) => <div> Hello {props.name} </div>
const Score = (props) => <div> Your score is {props.value} </div>
const el = <div>
<Hello name='Abhi' />
<Score value='100' />
</div>
ReactDOM.render(el, rootElement)
All attributes passed to this component are its props
JSX
As a template - composition
// Even better
const Hello = (props) => <div> Hello {props.children} </div>
const Score = (props) => <div> Your score is {props.children} </div>
const el = <div>
<Hello> Abhi </Hello>
<Score> 100 </Score>
</div>
ReactDOM.render(el, rootElement)
All child elements within this component are its props.children
JSX
As a template - composition
// Even better
const Hello = (props) => <div> Hello {props.children} </div>
const Score = (props) => <div> Your score is {props.children} </div>
let data = { name: 'Abhi', score: 75 }
const el = <div>
<Hello> { data.name } </Hello>
<Score> { data.score } </Score>
</div>
ReactDOM.render(el, rootElement)
Externally obtained data being used here
JSX
As a template - composition
// Even better
const Hello = (props) => <div> Hello {props.children} </div>
const Score = (props) => <div> Your score is {props.children} </div>
const ScoreCard = (props) => {
<div>
<Hello> { props.name } </Hello>
<Score> { props.score } </Score>
</div>
}
let data = { name: 'Abhi', score: 75 }
let el = <ScoreCard {...data} />
ReactDOM.render(el, rootElement)
The entire widget as a component, with data passed in using spread operator
JSX
As a template - composition - summary
- A function with capitalized name becomes a React component
- It can return Html tags or other custom components
- All attributes passed to this component are its props
- All child elements within this component are its props.children
- The look and feel is almost like HTML. Just that we cant use the class property
JSX
React.createElement
- All the JSX elements are converted into React.createElement calls
- Babel does this conversion
- This is also visible in the final source, in the browser
JSX
React.createElement
// JSX
var el = <div>Hello World</div>;
// Javascript
var el = React.createElement(
"div",
null,
"Hello World"
);
Try in http://babeljs.io/repl
JSX
React.createElement
// JSX
<div id='foo'>
<Hello> { props.name } </Hello>
</div>
// Javascript
React.createElement(
"div", // name in quotes
{ id: 'foo' }, // attributes
React.createElement(
Hello, // Capitalized, no quotes
null,
" ",
props.name,
" "
)
);
- Custom components, like Hello here capitalized, and are not within quotes
- Normal tags, like div are within quotes
- The attributes are passed as the second argument
JSX
JSX conditionals
// Conditionals in JSX
// The following does not work
let list = [];
<div>
{
if (list.length == 0){
<div> No items </div>
}
else {
<div> {list.length} items </div>
}
}
</div>
- What goes within JSX should be expressions - should evaluate to the expected value
- 'JSX is just syntactic sugar for function calls and object construction' - React docs
JSX
JSX conditionals
// Conditionals in JSX
// The following works
let list = [];
<div>
{
(list.length == 0) ?
<div> No items </div>
:
<div> {list.length} items </div>
}
</div>
- Ternary operators make expressions. Hence they work
- This kind of code is common
- Can be improved
JSX
JSX conditionals
// Conditionals in JSX
// The following works
const render = (list ) => {
let none = <div> No Items</div>
let itemList = <div> Items list </div>
if (list.length == 0) return none;
return itemList;
}
- Its better to keep conditionals out of JSX interpolations
JSX
JSX conditionals
// Conditionals in JSX
// The following works
let None = () => <div> No Items</div>
let ItemList = () => <div> Items list </div>
const render = (list ) => {
if (list.length == 0) return <None/>;
return <ItemList />;
}
- And even better - use smaller custom components
JSX
JSX loops
// Loops in JSX
// The following does not work -
<div>
{
for (var i=0; i < numrows; i++) {
<span> Item - {i}</span>
}
}
</div>
- Loops also don't work this way
- Reason is the same - JSX is just syntactic sugar for function calls and object construction
JSX
JSX loops
// Loops in JSX
// The following works -
<div>
{ list.map( item => <span>Item - {item} </span> ) }
</div>
- Use map function calls instead
- key is also needed
JSX
JSX loops
// Loops in JSX
// The following works -
const render = () => {
var rows = [];
for (var i = 0; i < numrows; i++) {
rows.push(<span key={i}>Item - {i} </span>);
}
return (<div>{rows}</div>);
}
- Or pre-generate the contents
JSX
JSX outside React
-
Since JSX is well specified () it can be implemented by libraries other than React
- https://facebook.github.io/jsx/
-
The jsx-transform project tries to decouple JSX from React, for use with the mercury framework
- https://github.com/alexmingoia/jsx-transform
- https://github.com/Raynos/mercury
JSX
React Virtual DOM
- React maintains an in-memory copy of the DOM - the Virtual DOM
- Changes are applied to this copy, and are passed on to the real DOM when needed
- Changes are applied as diffs. When there are no diffs, the DOM doesn't change.
- This is a great optimization, and makes React perform much better than traditional templates
</ppt>
JSX
By Abhishek Yadav
JSX
- 959