1_
Simplest webpage with React from CDN
The web page below uses React from CDN, creates root element by React functions and replaces elements in DOM
HTML
<html>
<body onload="myFunction()" />
<h1>React.js basics</h1>
<div id="demo" >This will be replaced by pure JavaScript</div>
<div id="react-app" >This will be replaced by React.js</div>
<div id="react-app1" >This will be replaced by React.js 1</div>
<script src="https://unpkg.com/react@15/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>
<script>
function myFunction() {
document.getElementById("demo").innerHTML = "Hello Wrold - Replaced by pure JavaScript";
var rootElement =
React.createElement('div', {},
React.createElement('h1', {}, "People"),
React.createElement('ul', {},
React.createElement('li', {},
React.createElement('h2', {}, "John First"),
React.createElement('a', {href: 'mailto:john@first.com'}, 'john@first.com')
),
React.createElement('li', {},
React.createElement('h2', {}, "Paul Second"),
React.createElement('a', {href: 'mailto:paul@second.com'}, 'paul@second.com')
)
)
)
ReactDOM.render(rootElement, document.getElementById('react-app'))
ReactDOM.render(rootElement, document.getElementById('react-app1'))
}
</script>
</body>
</html>
2_
What is JSX and how it compiles to JavaScript
JSX aloows embedding hmlt tag into JavaScrip.
JavaScript
var AboutPage = React.createClass({
render: funtion() {
return (
<div>
<h1 color="blue">About</h1>
<p>Demo page</p>
</div>
);
}
});
The JSX code with nested elements above compiles to JavaScript with React
JavaScript
React.createElement("div", null,
React.createElement("h1", {color:"blue"}, "About" ),
React.createElement("p", null, "Demo page"},
)
3_
How to create simple webpage with About page as component
Create about Page as a component
JavaScript
var React = require('react'); // includes React package
var AboutPage = React.createClass({
render: function() {
return (
<div>
<h1 color="blue">About</h1>
<p>Demo page</p>
</div>
);
}
});
module.Export = AboutPage; // makes the page available
Include that page in main page
JavaScript
var React = require('react');
var AboutPage = require('./components/aboutPage'); // includes AbouPage component
React.render(<AboutPage>, document.getElementById('root')); // renders AboutPage in root element
Create index.html that uses the app built above
HTML <html> <body> <div id="root"></div> <script src="scripts/bundle.js"></script> // gets created by gulp or webpack (all .js in one file) </body> </html>
4_
Differences in event handlers in ES5/6
Work in ES5
JavaScript
<div onClick={this.handleClick}/>
ES6 requires bind
JavaScript
<div onClick={this.handleClick.bind(this)}/>
It can be handled in class constructor
JavaScript
class People extends React.Component{
constructor(props) {
super(props);
this.handleClick=this.handleClick.bind(this);
}
ES5 Stateless component (since React 1.4)
JavaScript
var ContactPage = function(props) {
return (
<h1> Main Street 1, Small City</h1>
);
});
ES6 Stateless component
JavaScript
const ContactPage = (props) => {
return (
<h1> Main Street 1, Small City</h1>
);
});
Stateless components do not have Lifecycle methods
5_
How to manage state in React
Assuming there is a simple React component that renders first name from a Person class.
1. using forceUpdate
In constructor
2. using State
In constructor
Note: if you use an array, you cannot assign it directly to state, you have to use
Note: Redux is using the same principle, just requires more code.
3. Using @observable in MobX, please refer to the MobX section
1. using forceUpdate
In constructor
JavaScriptrender()
JavaScript this.Person.firstNameonClick()
JavaScript
this.person = new Person('Paul');
this.forceUpdate()
2. using State
In constructor
JavaScript
this.state = new Person('John');
render()
JavaScript this.state.person.firstNameonClick()
JavaScript
var newPerson = new Person('Paul');
this.setState(newPerson);
Note: if you use an array, you cannot assign it directly to state, you have to use
JavaScript
this.state = { "people" : people }
Note: Redux is using the same principle, just requires more code.
3. Using @observable in MobX, please refer to the MobX section
6_
How to create simple webpage with About page as component
Assuming you have multiple components/pages and you need to defined mapping between page and url.
You have to display contact page is user types http://mypages.com/#contact
You have to display contact page is user types http://mypages.com/#contact
JavaScript
var React = require('react');
var AboutPage = require('./components/aboutPage');
var Contact = require('./components/contact');
var AboutPage = React.createClass({
render: function() {
var Page;
switch (this.props.route) {
case 'contact' : Page = Contact; break;
default: Page = AboutPage;
}
return (
<div>
<Page/>
</div>
);
}
});
function render() {
var myRoute = window.location.hash.substr(1);
React.render(<App route = {myRoute} /> document.getElementById('root'));
}
window.addEventListener('hashchange', render); // listens to URL changes and renders new content
render(); // renders page first time
7_
How to use Router
Router provider centralized way of handling routes. Create myRoutes.js that contains all mapping among urls and components
JavaScript
var React = require('react'); // includes React package
var myRoutes = (
<Route name="app" path="/" handler={require('./main')}>
<DefaultRoute handler=={require('./components/aboutPage'}>
// if path is not set, name is used
<Route name="people" path="allPeople" handler=={require('./components/people'}>
</Route>
);
}
});
module.Export = myRoutes;
Update main.js to use myRoutes
JavaScript
var React = require('react');
var RouteHandler = require('react-router');
var myRoutes = require('./myRoutes');
var App = React.createClass({
render: function() {
return (
<div>
<RouteHandler/>
</div>
);
}
});
Router.run(myRoutes, function(Handler) {
React.render(<handler>, document.getElementById('root'));
});
8_
Describe properties and state
this.props.userName - immutable - passd to child components from top level component
this.state.username - data in controller view
9_
Lifecycle events
Lifecycle: componentWillMount - runs before initial renderning componentDidMount - runs after render (DOM exists) componentWillReceiveProps - run before new properties are received. Does not run on initial render. shouldComponentUpdate - runs before render. If returns falls, render will be ignored. componentWillUpdate - after rendering (when new props are states changed). SetState cannot be called here componentDidUpdate - after componts updates propageated to the DOM componentWillUnmount - before component is unmounted from the DOM
10_
how to display a list
JavaScript
var People = React.createClass({
// initial state is an empty array
getInitialState: function() {
return {
people: []
};
},
// loads data in json format and assigns them to people
componentWillMount: function() {
this.setState({ people: peopleService.getPeople() });
},
render: funtion() {
var createPersonText = function(person) {
return (
// id is necessary for the framework
<li key={author.id}>{person.firstName} {person.lastName}</li>
);
}
return (
<div>
<ul>
{this.state.people.map(createPersonText, this)}
</ul>
</div>
);
}
});
module.exports = People;
11_
What PropTypes are avaibale
PropTypes help to validate if required parameters or/and types were passed to the component.
React.PropTypes.object.isRequired React.PropTypes.func.isRequired React.PropTypes.bool React.PropTypes.number React.PropTypes.string React.PropTypes.array React.PropTypes.funcDo not run in minified version!
12_
Set property expectations with PropTypes
JavaScript
var PeopleList = React.createClass({
propTypes: {
people: ReactPropTypes.array.isRequired
},
render: funtion() {
...
If people array has not been passed then browser will show a warning.
13_
Create store in code with Root reducer
Configure store
configureStore.js
JavaScript
import {createStore} from 'redux';
import rootReducer from './myReducers';
export default function configureStore(initialState) {
return createStore(
rootReducer,
initialState
}
Instantiate store for the app in src/index.js
JavaScript
import configureStore from './store/configureStore'
import {Provider} from 'react-redux'
const store = configureStore();
render(
<Provider store={store}>
<Router history={browseHistory routes={routes} />
</Provider>,
document.getElementById('root')
);
14_
Create action for 'create person' operation
JavaScript
export function createPerson(person) {
return { type: 'CREATE_PERSON', person};
}
if the action should be reused, definig the action as a const helps avoiding type error
JavaScript // consts defined in an independent file export const CREATE_PERSON = 'CREATE_PERSON';
JavaScript
import * as types from './actiontypes';
export function createPerson(person) {
return { type: types.CREATE_PERSON, person};
}
15_
Describe reducers in context of create person functionality
Reducers are called by dispatch() method of store and return modified data as NEW oject
JavaScript
function myReducer(state, action) {
switch (action.type) {
case 'SET_NAME':
return (Object.assign({}, state, { name: 'John' });
default:
return state;
}
}
reducers/index.js
function arrayReducer(state =[], action) {
switch (action.type) {
case types.CREATE_PERSON:
return [...state, Object.assign({}, action.person)];
default:
return state;
}
}
16_
Describe Root reducer
Using Root reducer simplifies multi case statements
JavaScript
import {combineReducers} from 'redux';
import myReducer from './myReducerFile';
const rootReducer = combineReducers({
myReducer : myReducer
});
export default rootReducer;
17_
Simplest way using Redux
Assuming to have a page like this
JavaScript
import React,{PropTypes} from 'redux';
class People extends React.Component{
constructor(props) {
super(props, contect);
this.state = {
person:{ name:""}
};
this.OnNameChanged = this.OnNameChanged.bind(this);
this.OnClickCreate = this.OnClickCreate.bind(this);
}
onNameChanged(event) {
const person = this.state.person;
person.name = event.target.value;
setState({person:person});
}
onClickCreate() {
alert('${this.state.person.name} would be created here');
}
render() {
return (
<div>
<input type="text" onChange={this.onNameChanged} value{this.state.course.title} />
<input type="submit" onClick={this.onClickCreate} value="Create" />
</div>
);
}
}
export default PeoplePage;
If you start using Redux it will change like this:
JavaScript
import React,{PropTypes} from 'redux';
import {connect} from 'react-redux';
import * as personAction from './personActions';
class PeoplePage extends React.Component{
constructor(props) {
...
}
onClickCreate() {
// props.dispatch is injected by connect(), if connect uses only 1 argument
this.props.dispatch(personAction.createPerson(this.state.person));
}
personDetail(person, index) {
return <div key={index}>{person.name}</div>
}
render() {
return (
<div>
{this.props.people.map(this.personDetail)} //map() iterates over list - this line displays list
...
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
people: state.people
};
}
export default connect(mapStateToProps)(PeoplePage);
18_
Data flow with Redux after user clicks createPerson button
Flow:
REACT
- user clicks "Create" button
- personPage.js onClickCreate() calls this.props.dispatch(personAction.createPerson(this.state.person))
ACTION
- personAction.js createPerson(aPerson) returns { type='CREATE_PERSON', person=aPerson }
- dispatch() calls all reducers
REDUCERS
- personReducer.js personReducer() returns new array with the person passed addedd into the array
REACT
- personPage.js mapStateToProps(state. ownProps) returns { people: state.people}
- personPage.js render() draws array in this.props.people
19_
How to use mapDispatchToProps()
JavaScript
import React,{PropTypes} from 'redux';
import {connect} from 'react-redux';
import * as personAction from './personActions';
class PeoplePage extends React.Component{
constructor(props) {
...
}
PeoplePage.PropTypes = {
people: PropTypes.array.isRequired,
createPerson: PropTypes.func.isRequired
}
onClickCreate() {
this.props.createPerson(this.state.person);
// before was: this.props.dispatch(personAction.createPerson(this.state.person));
}
personDetail(person, index) {
return <div key={index}>{person.name}</div>
}
render() {
return (
<div>
{this.props.people.map(this.personDetail)} //map() iterates over list - this line displays list
...
</div>
);
}
}
function mapStateToProps(state, ownProps) {
return {
people: state.people
};
}
function mapDispatchToProps(dispatch) { // "dispatch" gets injected by the "connect()" function
return {
createPerson: person => dispatch(personActions.createPerson(person))
};
}
export default connect(mapStateToProps, mapDispatchToProps)(PeoplePage);
20_
How to use bindActionCreators
JavaScript
import React,{PropTypes} from 'redux';
import {connect} from 'react-redux';
import * as personAction from './personActions';
import {bindActionCreators} from 'redux';
class PeoplePage extends React.Component{
constructor(props) {
}
PeoplePage.PropTypes = {
people: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired,
}
onClickCreate() {
this.props.actions.createPerson(this.state.person);
}
render() {
return (
...
);
}
}
function mapStateToProps(state, ownProps) {
return {
people: state.people
};
}
function mapDispatchToProps(dispatch) { // "dispatch" gets injected by the "connect()" function
return {
actions: bindActionCreators(courseActions, dispatch); mapping all actions in personActions.js
// before was: createPerson: person => dispatch(personActions.createPerson(person));
};
}
export default connect(mapStateToProps, mapDispatchToProps)(PeoplePage);
21_
How to install MobX
To install MobX run following
CMD npm install mobx --save npm install mobx-react --saveif using TypeScript, set experimentalDecorators to true
JSON
tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true
...
22_
How manage state with MobX
JavaScript
@observer
class PersonPanel {
@observable person = new Person('John')
...
}
render()
JavaScript this.Person.firstName
JavaScript
onClick() {
this.person = new Person('Paul')
}
23_
How to confige Node for TypeScript debugging - seeing typeScript code
In order to be able to see TyleScript code instead of compiled JavaScript code in debugger you need to add source mapper.
JSON
package.json
"source-map-loader": "^0.1.5"
JSON
tsconfig.json
"compilerOptions": {
"sourceMap": true,
JSON
webpack.config.dev.js
// Enable sourcemaps for debugging webpack's output.
devtool: "source-map",
module: {
preLoaders: [
// All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
{ test: /\.js$/, loader: "source-map-loader" }
]
},
24_
How to use jQuery with TypeScript 1
25_
How to use jQuery with TypeScript 2 with node.js
JavaScript 2 does not require to reference .d.ts in the code file. It uses a reference in package.json
To search if a types are available for a package go to npmjs.com or http://microsoft.github.io/TypeSearch/
If yes, then run
To search if a types are available for a package go to npmjs.com or http://microsoft.github.io/TypeSearch/
If yes, then run
JSON
npm install --save @types/<package>
or update package.json"
TypeScript
"devDependencies": {
"@types/<package>": "<version>"
}
and run
JSON
npm install
to download the types. The types will be downloaded under node_module\@types\<package>
26_
How install and configure Mocha
To install Mocha, update package.json:
JSON
package.json
{
"devDependencies": {
...
"@types/mocha": "^2.2.34",
"mocha": "2.4.5",
...
}
}
In order to run Mocha, update package.json
JSON
package.json
{
"scripts": {
"pretest": "tsc",
"test": "mocha \"dist/**/*.test.js\"",
...
},
The "pretest" step compiles all typescript files (based on tsconfig.js) into dist folder and the "test" steps will execute the test based on *.test.js file pattern27_
How write a dummy test for Mocha
Let's write a test. A dummy test to start to show the basics. 1 pass, 1 fail
TypeScript
export class TestException {
message: string;
constructor(aMessage : string) {
this.message = aMessage;
}
};
describe('Dummy test demo', () => {
it('should PASS', () => {
// does nothing
});
it('should FAIL', () => {
throw new TestException("My failed test message");
});
});
Run the tests from command line by executing
CMD npm test
28_
Write a test to test add() function
Now something more real. Assuming there is a method add that needs to be tested
TypeScript
import * as myLib from './myLib';
describe('MyLib', () => {
it('WHEN add() called THEN correct value returned', () => {
// act
var result = myLib.add(1, 2);
// assert
var expected = 3;
if (result != expected) throw new TestException(result + " != " + expected);
});
});
29_
How to write test setup or tear down
TypeScript
export class TestException {
message: string;
constructor(aMessage : string) {
this.message = aMessage;
}
};
// Setup / Tear down
before(function(){
console.log(' Runs one time before the first test in the file')
})
after(function(){
console.log(' Runs one time after all tests in the file were executed')
})
beforeEach(function(){
console.log(' Runs before each test in the file')
})
afterEach(function(){
console.log(' Runs after each test in the file')
})
describe('Dummy test demo with setup/tear down', () => {
it('should PASS', () => {
// does nothing
});
it('should FAIL', () => {
throw new TestException("My failed test message");
});
});
30_
How to write custom test suite with setup or tear down
TypeScript
function makeTestSuite(name:string, tests:any) {
describe(name, function () {
before(function () {
console.log(" executed before each test in the tst suite");
});
tests();
after(function () {
console.log(" executed before each test in the test suite");
});
});
}
makeTestSuite('Suite 1 - with setup', function(){
it('Test 1', function(done){
done();
});
it('Test 2', function(done){
done();
});
});
describe('Test Suite2 - without setup', function(){
it('Test 3', function(done){
done();
});
});
31_
Install Expect and write a test for add() function
To install Expect, modify package.json
JSON
package.json
"devDependencies": {
"@types/expect": "^1.13.31",
"expect": "1.19.0",
...
}
TypeScript
import * as myLib from './myLib';
import * as expect from 'expect';
describe('MyLib:', () => {
it('WHEN add() called THEN correct value returned', () => {
// act
var result = myLib.add(1, 2);
// assert
expect(result).toEqual(3);
});
});
32_
Install Chai and write a test for add() function
To install Expect, modify package.json
JSON
package.json
"devDependencies": {
"@types/chai": "3.4.30",
"chai": "^3.5.0"
...
}
TypeScript
import * as myLib from './myLib';
import * as chai from 'chai';
chai.should();
var expect = chai.expect;
var assert = chai.assert;
describe('MyLib:', () => {
it('WHEN add() called THEN correct value returned', () => {
// act
var result = myLib.add(1, 2);
// assert
// Option 1
expect(result).to.equals(3);
// expect(result).toEqual(3); 'expect' library style
// Option 2
result.should.equal(3);
// Option 3
assert.equal(result, 3);
});
});
33_
How to mock calls with 'expect' library'
TypeScript
import * as expect from 'expect';
// method to be mocked
var myService = {
getValue: function () {}
}
describe('MyService:', () => {
// bypass call and return value
it('WHEN getValue() called THEN 3 returned', () => {
// arrange
var spy = expect.spyOn(myService, 'getValue')
var dice = spy.andReturn(3)
// act
var result = myService.getValue();
// assert
spy.restore()
expect(result).toEqual(3);
});
// fail if not called
it('WHEN test runs THEN FAIL if getValue() was not called', () => {
// arrange
var spy = expect.spyOn(myService, 'getValue')
// assert
expect(spy).toHaveBeenCalled('not called')
spy.restore()
});
});
34_
Compare Chai and Expect assertion libraries
- It integrates mocking and stubbing functionality, which limits configuration problem issues if these were in 2 separated libraries
- It limits syntax options how to write asserts, which will lean to cleaner
- Recommended by Cory House (author of Pluralsite React-Redux course - assessed with 5 stars)
Feature Chai Expect should() syntax x expect() syntax x x assert syntax x integrated mocking x extensions ? xRecommendation to use Expect because:
- It integrates mocking and stubbing functionality, which limits configuration problem issues if these were in 2 separated libraries
- It limits syntax options how to write asserts, which will lean to cleaner
- Recommended by Cory House (author of Pluralsite React-Redux course - assessed with 5 stars)