What's react again? →
On its own React is a library for generating the user interface of an application.
What did we use to demonstrate our small React examples? →
We used Codepen (jsbin is also an option). However… we had to do a little bit of configuration first. →
ReactElements are elements in a virtual DOM. Here's what creating a ReactElement looks like… →
React.createElement('h1', {className: 'hello'}, 'Hi There!');
createElement
has 3 parameters:
Changes in the virtual DOM are combined together and applied to the actual DOM in a way that minimizes DOM modification. Here's what rendering a ReactElement looks like. →
ReactDOM.render(
React.createElement('h1', {className: 'hello'}, 'Hi there!'),
document.body
);
render
has two arguments
ReactElement
HTMLElement!
)Let's give it a try. →
Sooo… there was another unusual way of creating ReactElements. What was it? →
Using JSX, we could… →
ReactDOM.render(
Hi there!
,
document.body
);
Why did that look so unusual? →
ReactDOM.render(
Hi there!
,
document.body
);
Hey - there's markup in my JavaScript. It's an unquoted string! What!?
So, what's JSX again? →
JSX is an extension to JavaScript syntax that allows XML-like syntax without
So that means… this JSX
Hi there!
…is equivalent to this vanilla JavaScript
React.createElement('h1', {className: 'hello'}, 'Hi there!'),
They both produce a ReactElement!
You can bundle elements together into a single component. Here's an example. →
const MyComponent = React.createClass({
render: function() {
return (
<div> <h1>A Message</h1>{this.props.message}</div>
);
}
});
ReactDOM.render(
<MyComponent message="Hi there!" />,
document.body
);
To make a component, use React.createClass
, which takes an object as a parameter or use ES6 classes.
Let's try to create a component that… →
greet
is true
For example… rendering…
<MyComponent greet={true} />,
Gives us
hi
Changing greet
to false would give us bye
instead.
const MyComponent = React.createClass({
render: function() {
const msg = this.props.greet ? 'hi' : 'bye';
return (
<h1>{msg}</h1>
)
}
});
ReactDOM.render(
<MyComponent greet={true} />,
document.body
);
To add an event handler in JSX… add an inline attribute (wait, what!?). For example, click events would be represented by onClick
:
const MyButton = React.createClass({
onButtonClick: function(evt) {
alert("Clicked!");
},
render: function() {
return <div onClick={this.onButtonClick}>Press This Button</div>;
}
});
ReactDOM.render(
<MyButton />,
document.body
)
Conventions and notes when handling events →
If the stuff we're starting to do is too complicated to debug with codepen, another easy way of working with react is using create-react-app →
To use it: →
npm install -g create-react-app
create-react-app projectname
App.js
and App.css
in src
npm start
Now we have 3 options for developing super simple react apps →
The above options are in-order of easiest to debug. However, note that:
state is internal data controlled by the component (contrast this with props, which are controlled by whatever renders the component).
To initialize state properties when your component is created:
getInitialState
within your component definition…
{ // within a component definition
getInitialState: function() {
return {
prop: val,
}
}
}
Note that we'll take a look at setting initial state with ES6 classes a little later!
Once your application has state, you can manipulate it by reading or setting it →
this.state.propertyName
this.setState({stateName: stateValue});
When setting state, you must pass an object.
The object passed in specifies the new values of properties.
Let's define a couple of state variables, and put their values in a list when the component is rendered. →
const MyComponent = React.createClass({
render: function() {
return (
- {this.state.var1}
- {this.state.var2}
)
},
getInitialState: function() {
return {
var1: 'this is the first variable',
var2: 'and a second variable'
}
}
});
Things are slightly different if you're using ES6 Classes to define your components →
getInitialState
, create a constructor that sets the state property to an object:this
works (createClass
handles it for you, but ES6 classes do not)getInitialState
to constructor
Instead of defining getInitialState
… →
Create a constructor (optionally defining a props parameter), call super
and assign an object to this.state
constructor() {
super();
this.state = {
title: 'A React Component'
};
}
Pass in props
to your constructor if you want access to "attributes" (this.props
isn't available within the constructor yet, so props can be accessed via parameter):
constructor(props) { ... }
createClass
and this
Let's check out an event example with createClass
. There's something a little suspicious about the code - what's weird about it? →
const MyButton = React.createClass({
getInitialState: function() {
return { msg: 'Clicked!!!' };
},
handleClick: function(evt) { alert(this.state.msg); },
render: function() {
return <div onClick={this.handleClick}>Press Me!</div>;
}
});
this
should refer to the global object, but it doesn't; everything works just fine!createClass
, this
is automatically set to the instance of the created ReactElement
for us!Unfortunately, ES6 classes do not set this
for us like createClass
does. Consequently, the code below, which looks equivalent, will not work! →
class MyButton extends React.Component {
constructor() {
super();
this.state = { msg: 'Clicked!!!' };
}
// Y U NO WORK??????
handleClick(evt) { alert(this.state.msg); }
render() {
return <div onClick={this.handleClick}>Press Me!</div>;
}
}
this
Let's modify the following lines so that this
is bound to the instance rather than the global object. →
handleClick(evt) { alert(this.state.msg); }
render() {
return <div onClick={this.handleClick}>Press Me!</div>;
}
<div onClick={this.handleClick.bind(this)}>
handleClick
in an arrow function to capture render
's this
:
<div onClick={() => {this.handleClick()}}>
Using events and state, create a component that:
Start off with some boiler plate…
const MyComponent = React.createClass({
// render, getInitialState and event handler
// goes here...
});
ReactDOM.render(
<MyComponent />,
document.body
);
Within your component definition:
getInitialState: function() {
return {
count: 0,
}
}
handleClick: function() {
this.setState({count: this.state.count + 1});
}
render: function() {
return (
<div className="number"
onClick={this.handleClick}>{this.state.count}</div>
)
}
Now let's try adding 3 counters to our render… →
ReactDOM.render(
<div><Counter /><Counter /><Counter /></div>,
document.body
);
class Clicker extends React.Component {
constructor() {
super();
this.state = {count: 0};
}
render() {
return <div onClick={() => {this.handleClick()}}>{this.state.count}
</div>;
}
handleClick() {
this.setState({count: this.state.count + 1});
}
}
When you're actually writing real components →
The common pattern for this is to: →
From the react docs: When you want to →
Move the state upwards so that it lives in the parent component: →
In this example, the child component doesn't define its own click handler, but instead receives one from its parent via props. →
class Parent extends React.Component {
// this handleClick will be used by the child!
handleClick() {
alert('cliiiiick me');
}
render() {
// pass handlClick to child as a prop
return <Child onClick={this.handleClick} />
}
}
class Child extends React.Component {
render() {
return (<div onClick={this.props.onClick}>
PRESS ME</div>);
}
}
ReactDOM.render(<Parent />, document.body);
Let's try rewriting our counter so that the state is moved up to a parent element. →
Start with our parent class…
class Parent extends React.Component {
}
Within that parent class, define a constructor to create initial state.
constructor() {
super();
this.state = {
count:0
}
}
handleClick
Now let's define handleClick
→
handleClick
sets the state like it did previouslythis
!
handleClick() {
this.setState({count: this.state.count + 1});
}
render
render
creates a child element and passes down a click handler and a count as props →
render() {
// note that we have to bind this so that we have
// access to the this of the instance that render is
// called on(and consequently this.state!
const handler = this.handleClick.bind(this);
return (
<Child onClick={handler} val={this.state.count} />
);
}
bind
to bind context of handleClick to instanceonClick={() => {this.handleClick()}}
If you're worried about making too many function objects, you can set this.handleClick to the bound version in the constructor rather than each time render
is called →
constructor() {
// add this line...
this.handleClick = this.handleClick.bind(this);
}
render() {
// change back to using this.handleClick
return (
<Child onClick={this.handleClick} val={this.state.count} />
);
}
Finally, for the child component, use props to define its click handler and its text content. →
class Child extends React.Component {
render() {
return (
<div onClick={this.props.onClick}>
{this.props.val}
</div>);
}
}
Of course, call render
:
ReactDOM.render(<Parent />, document.body);
Your event handler may require an argument. For example, it's a way to specify which child component was clicked without the child explicitly
// passing args to handle click, fixing arity, computer property names
class Parent extends React.Component {
}
Again, start with a constructor, but now we have state for two click boxes!
constructor() {
super();
this.state = {
box1:0,
box2:0
}
}
Notice that our handleClick function will take an argument, the name of the box: →
handleClick(name) {
// setState will be called based on this name!
// this is using shorthand syntax for dynamic keys:
this.setState({[name]: this.state[name] + 1});
}
Lastly, when we define render, note that we're passing in different arguments to the call to handleClick.
render() {
return (
<Child onClick={() => {this.handleClick('box1')}} val={this.state.box1} />
<Child onClick={() => {this.handleClick('box2')}} val={this.state.box2} />
);
}
And, of course, our Child component code:
class Child extends React.Component {
render() {
return <div onClick={this.props.onClick}>{this.props.val}</div>;
}
}
Rendering …
ReactDOM.render( , document.body);
Let's change our original clicker so that: →
Parent Count: 4
Child Count: 4
ClickCounter
This component is the parent; you can see that it: →
onClick
function via props
class ClickCounter extends React.Component {
constructor() {
super();
this.state = { count: 0 };
}
handleClick(arg) {
alert(arg + ' ' + this.state.count);
this.setState( { count: this.state.count + 1} );
}
render() {
// wrap onclick callback in arrow function to handle this
return Parent Count: {this.state.count}
<Clicker count={this.state.count} onClick={() => {this.handleClick("clicked!")}} />;
}
}
Clicker
Aaaand here's our child implementation →
class Clicker extends React.Component {
render() {
return <div onClick={() => {this.props.onClick()}}>Child Count:{this.props.count}</div>;
}
}
Of course… rendering:
ReactDOM.render(
,
document.body
)
Let's try building this component… →
Form input elements have their own state (for example, when you type into the text field, that element's state is what you typed →
In next few slides, the demo will will be using an input element with an onChange attribute.
Let's create an h3 that has its content controlled from a text input field. The h3's text value will change as data is typed in the text field.
First, let's create our h3 as a component →
class Message extends React.Component {
render() {
let value = this.props.value;
if(value === '') {
value = "Type Something PLZ ^^^";
}
return {value}
}
}
Then we'll deal with onChange and state. In a component called InputDemo… →
constructor() {
super();
this.state = {message: ''};
this.handleChange = this.handleChange.bind(this);
}
handleChange(evt) {
this.setState({message: evt.target.value});
}
render() {
return (
Message: <input value={this.state.message} onChange={this.handleChange} type="text" />
<Message value={this.state.message} />
);
}
If a component only deals with props, you can use a function instead of an ES6 style class or createClass →
function MyComponent(props) {
return {props.message}
}
Instead of:
class MyComponent extends React.Component {
render() {
return {this.props.message}
}
}
Implement the method componentDidMount
to run code after a component is mounted to the actual DOM.
Why is this useful? The React Component docs specify that this is where background requests should be placed:
componentDidMount
is meant for mutating the existing DOM