We use CodeSandbox for this interactive session.
JSX is just syntax sugar for some React JavaScript API. For example, the following JSX code ...
<DashboardUnit data-index="2">
<h1>Scores</h1>
<Scoreboard className="results" scores={gameScores} />
</DashboardUnit>;
gets transpiled to ...
React.createElement("div", { className: "red" }, "Children Text");
React.createElement(MyCounter, { count: 3 + 5 });
React.createElement(
DashboardUnit,
{ "data-index": "2" },
React.createElement("h1", null, "Scores"),
React.createElement(Scoreboard, { className: "results", scores: gameScores })
);
It's basically writing html in JavaScript. There are some difference though, for example, instead of using class
, you should use className
instead because class
is ES6 reserved keyword.
In Hello.js
, change the JSX syntax to plain JS and see what happens.
import React from 'react';
export default ({ name }) => React.createElement(
'h1', {}, `Welcome to ${name}`
);
You can add props as the second parameter.
import React from 'react';
export default function ({ name }) {
const props = {
'className': 'underlined'
};
return React.createElement(
'h1', props, `Welcome to ${name}`
);
};
And add CSS to the html.
<style>
.underlined {
text-decoration: underline;
}
</style>
A JavaScript object that transfrom props and states into html. Let's try to add a barebone component to our App.
class TodoList extends React.Component {
render () {
return (
<div>
This is a TodoList
</div>
);
}
};
const App = () => (
<div style={styles}>
<Hello name="CodeSandbox" />
<h2>This is an awesome Todo List!</h2>
<TodoList/>
</div>
);
We can access props via this.props
in a component instance. render
is the function that converts state
(see below) and props
into JSX (aka html).
class TodoList extends React.Component {
render () {
return (
<div>
This is a TodoList. It's made by {this.props.author}
</div>
);
}
};
const App = () => (
<div style={styles}>
<Hello name="CodeSandbox" />
<h2>This is an awesome Todo List!</h2>
<TodoList author="diwu"/>
</div>
);
Every compoenent has a object state
. This is ... it's state! =)
We can initial state in constructor
and update state using setState
. setState
will trigger another component lifecycle so render
gets called again.
class TodoList extends React.Component {
constructor(props) {
super(props)
this.state = {
'likeCount': 0
}
}
onLike() {
this.setState({
'likeCount': this.state.likeCount + 1
})
}
render () {
return (
<div>
<div>
This is a TodoList. It's made by {this.props.author}. {this.state.likeCount} people liked it!
</div>
<div>
<button onClick={this.onLike.bind(this)}>Like!</button>
</div>
</div>
)
}
}
Note: I made up the term "Single State Model" :')
The idea here is to have a single global state and pass it to the top level component (in our case, <App/>
).
let store = {
'todos': [
// {'description': 'this is a description'}
]
};
And yes, we don't need a 'done' key in that todo object. TODOs are never done.
Let's add a form for adding todo.
<form>
<input type="text" name="todo-description" id="todo-description" onChange={this.onTodoDescriptionChange.bind(this)}></input>
<button onClick={this.onAdd.bind(this)}>Add</button>
</form>
We will also need to add a todoDescription
key to the component state.
this.state = {
'likeCount': 0,
'todoDescription': '',
}
onTodoDescriptionChange
is called when the <input/>
text is changed, onAdd
is called when new todo item is added.
onAdd() {
store['todos'].push({
'description': this.state.todoDescription,
})
this.forceUpdate()
}
onTodoDescriptionChange(evt) {
this.setState({
'todoDescription': evt.target.value,
})
}
Finally we get to render the list of todos to the view using {store.todos.map}
.
<ul>
{store.todos.map(function (todo) {
return <li>{todo.description}</li>
})}
</ul>
Practice: Move
likeCount
to global state.
We sometimes will still have component level state because they just don't make sense to be global (like the todoDescription
above).