Virtual DOM
Components, Components, Components
React Components
import React from 'react';
import Menu from './Menu';
import SideMenu from './SideMenu';
import SideMenuItem from './SideMenu/SideMenuItem'
export default const App = () => {
return (
<SideMenu>
<SideMenuItem title="Item 1" icon="fa fa-home">
<p>Content for side menu item 1</p>
</SideMenuItem>
</SideMenu>
<Main>
</Main>
);
};
React Components
Server-side rendering, React Native
app.get('/', function(req, res){
res.render('index', {
react: ReactDOM.renderToString(HelloMessage({name: "John"}))
})
})
Server-side render
React Native
DOM-like Syntactical sugar
const el = (
<div>
<p>Hi!</p>
<MyComponent/>
</div>
)
const el = React.DOM.div(null,
React.DOM.p(null, "Hi!"),
MyComponent(null)
);
DOM-like Syntactical sugar
class is now className
const el = (
<div className="heading">
<p>Hi!</p>
<MyComponent/>
</div>
)
const el = (
<div class="heading">
<p>Hi!</p>
<MyComponent/>
</div>
)
function getGreeting(user) {
if (user) {
return <h1>Hello, {formatName(user)}!</h1>;
}
return <h1>Hello, Stranger.</h1>;
}
Expressions go into {}, with access to current JavaScript scope:
Conditional Rendering
const loading = true;
const renderLoadingScreen = () => {
if (loading) {
return <LoadingComponent/>
} else {
return <p>Hi!</p>;
}
};
const el = (
<div>
{ renderLoadingScreen() }
</div>
)
const loading = true;
const el = (
<div>
{ loading ? <LoadingComponent/> : '<p>Hi!</p>' }
</div>
)
const loading = true;
const renderLoadingScreen = () => {
if (loading) {
return <LoadingComponent/>
} else {
return <p>Hi!</p>;
}
};
const el = (
<div>
{ renderLoadingScreen() }
</div>
)
Conditional Rendering
Ternary short-hand:
const skills = ['javascript', 'photoshop'];
const renderChildren = () => {
return skills.map(skill => <span>{skill}</span>);
};
const el = (
<div>
<p>Skills: { renderSkills() }</p>
</div>
)
Rendering data sets is done using .map()
Data flows down
Events bubble up
Stateless component return a function
import React from 'react';
import CountDownTimer from './CountDownTimer';
export const App = () => {
return (
<div className="app">
<CountDownTimer></CountDownTimer>
</div>
)
};
import React from 'react';
const CountDownTimer = () => {
return <h1>05:00</h1>
};
export default CountDownTimer;
import React from 'react';
import CountDownTimer from './CountDownTimer';
export const App = () => {
return (
<div className="app">
<CountDownTimer></CountDownTimer>
</div>
)
};
Stateless component return a function
Result:
Data is passed via "props":
import React from 'react';
import CountDownTimer from './CountDownTimer';
export const App = () => {
return (
<div className="app">
<CountDownTimer startTime="5"></CountDownTimer>
</div>
)
};
import React from 'react';
import CountDownTimer from './CountDownTimer';
export const App = () => {
return (
<div className="app">
<CountDownTimer startTime="5"></CountDownTimer>
</div>
)
};
import React from 'react';
const CountDownTimer = (props) => {
return <h1>{props.startTime}</h1>
};
export default CountDownTimer;
Data is passed via "props":
props are accessible inside the component:
Result:
import React from 'react';
import moment from 'moment';
export const CountDownTimer = (props) => {
const startTime = moment(props.startTime, 'mm').format('MM:SS');
return <h1>{startTime}</h1>
};
Using NPM libs to manipulate props:
npm install --save moment
import React from 'react';
import moment from 'moment';
export const CountDownTimer = (props) => {
const startTime = moment(props.startTime, 'mm').format('MM:SS');
return <h1>{startTime}</h1>
};
npm install --save moment
Using NPM libs
<User
name="Anzor"
id={10}
employed={true}
skills={['javascript', 'photoshop', '3d']}
employer={{ name: 'BuildDirect' }}
photo={<UserPhoto id={10}></UserPhoto>}>
</User>
Prop types include String, Number, Boolean, Object, Array, other React Components
const User = (props) => {
const renderSkills = () => {
return props.skills.map(skill => <p>{skill}</p>);
};
return (
<div className="user">
<div>{props.photo}</div>
<p>{props.name}</p>
<p>{props.id}</p>
<p>{props.employer.name</p>
{renderSkills()}
</div>
)
}
Handling props
...
}
User.propTypes = {
id: React.PropTypes.number.isRequired,
employer: React.PropTypes.object.isRequired,
name: React.PropTypes.string.isRequired,
skills: React.PropTypes.array,
employed: React.PropTypes.bool.isRequired,
photo: React.PropTypes.element.isRequired
}
export default User;
Typechecking with Props
...
}
User.propTypes = {
id: React.PropTypes.number.isRequired,
employer: React.PropTypes.object.isRequired,
name: React.PropTypes.string.isRequired,
skills: React.PropTypes.array,
employed: React.PropTypes.bool.isRequired,
photo: React.PropTypes.element.isRequired
}
export default User;
Typechecking with Props
Stateful component is a class
import React, { Component } from 'react';
import moment from 'moment';
export class CountDownTimer extends Component {
constructor(props) {
super(props);
// initialize state to what's passed in
this.state = {
time: moment(props.startTime, 'mm')
};
}
render() {
// render value stored in state
const startTime = this.state.time.format('mm:ss');
return <h1>{startTime}</h1>
}
};
this.setState() triggers a re-render of virtual DOM
import React, { Component } from 'react';
import moment from 'moment';
export class CountDownTimer extends Component {
constructor(props) {
super(props);
this.state = {
time: moment(props.startTime, 'mm')
};
setInterval(() => {
const time = this.state.time.subtract({ seconds: 1 });
this.setState({ time });
}, 1000);
}
render() {
const startTime = this.state.time.format('mm:ss');
return <h1>{startTime}</h1>
}
};
Adding Timer logic using setState()
...
start() {...}
render() {
return (
<div className="app">
<CountDownTimer startTime="5"></CountDownTimer>
<button onClick={this.start}>Start Timer</button>
</div>
)
}
...
DOM events: onClick, onChange, etc...
import React, {Component} from 'react';
import CountDownTimer from './CountDownTimer';
export class App extends Component {
countDownTimerComplete() {
alert('time\'s up!');
}
render() {
return (
<div className="app">
<CountDownTimer
startTime="5"
onTimerDone={this.countDownTimerComplete}>
</CountDownTimer>
</div>
)
}
};
Custom events:
Emitting custom events:
class CountDownTimer extends Component {
constructor(props) {
super(props);
this.state = {};
}
...
decrement() {
if (time === 0) {
// emit event by executing the function
this.props.onTimerDone();
...
}
}
...
Typechecking function props:
class CountDownTimer extends Component {...}
CountDownTimer.propTypes = {
onTimerDone: React.PropTypes.func.isRequired
}
export default CountDownTimer;
aka "transclude"
import TabHeader from './TabHeader';
const UI = () => {
...
<Tabs>
<TabHeader>Tab 1 Content</TabHeader>
...
<Tabs>
};
const TabHeader = (props) => {
<div className="tab-header">{props.children}</div>
}
<div className="tab-header">Tab 1 Content</div>
const TabHeader = (props) => {
<div className="tab-header">{props.children}</div>
}
import TabHeader from './TabHeader';
const UI = () => {
...
<Tabs>
<TabHeader>Tab 1 Content</TabHeader>
...
<Tabs>
};