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 availableInclude 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 elementCreate 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 installto 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 pattern
27_
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)