All notes
Reac

Get started



sudo npm install -g create-react-app
create-react-app --version
create-react-app hackernews
cd hackernews/

npm start # Will start at localhost:3000
npm test
npm run build

Concepts

Virtual Dom

stackoverflow: how react js speeds up rendering with a virtual DOM.

What react's virtual DOM does, is calculate differences between one state of the DOM and the next state and minimizes DOM updates in a very smart way.

So:
DOM itself is not slow
but layout is slow
and almost all DOM updates require layout updates
so less DOM updates is faster

Why React Redux

css-tricks.com: learning react redux.

Redux's three guiding principals:

Single source of truth: redux store



// The Reducer Function
var userReducer = function(state, action) {
  if (state === undefined) {
    state = [];
  }
  if (action.type === 'ADD_USER') {
    state.push(action.user); // TODO: here we change state. See the next section for improvement.
  }
  return state;
}

// Create a store by passing in the reducer
var store = Redux.createStore(userReducer);

// Dispatch our first action to express an intent to change the state
store.dispatch({
  type: 'ADD_USER',
  user: {name: 'Dan'}
});

store.getState();   // => [{name: 'Dan'}]

// The reducer is actually called twice in the example — once when the store is created and then again after the dispatch.

State is read-only in reducers



var userReducer = function(state = [], action) {
  if (action.type === 'ADD_USER') {
    var newState = state.concat([action.user]);
    return newState;
  }
  return state;
}

JavaScript's primitive data types (Number, String, Boolean, Undefined, and Null) are already immutable. Objects, arrays, and functions are mutable.


// Example One
state.foo = '123';

// Example Two
Object.assign(state, { foo: 123 });

// Example Three
var newState = Object.assign({}, state, { foo: 123 });

// The first and second examples mutate the state object.

// The object "spread operator" is another way to keep the state immutable:
const newState = { ...state, foo: 123 };

// Object.assign() and spread operators are both ES2015.

Multiple reducers


import { createStore, combineReducers } from 'redux';

// The User Reducer
const userReducer = function(state = {}, action) {
  return state;
}

// The Widget Reducer
const widgetReducer = function(state = {}, action) {
  return state;
}

// Combine Reducers
const reducers = combineReducers({
  userState: userReducer,
  widgetState: widgetReducer
});

const store = createStore(reducers);

The use of combineReducers() allows us to describe our store in terms of different logical sections and assign reducers to each section.

Time travel

Why we need the second argument for createStore() which is for "initial state" if reducers can create initial state

Imagine a user does a refresh on your SPA and the store's state is reset to the reducer initial states. This might not be desired.

Instead, imagine you could have been using a strategy to persist the store and then you can re-hydrate it into Redux on the refresh. This is the reason for sending initial state into createStore().

Example: Container without Redux



import React from 'react';
import axios from 'axios';
import UserList from '../views/list-user';

const UserListContainer = React.createClass({
  getInitialState: function() {
    return {
      users: []
    };
  },

  componentDidMount: function() {
    axios.get('/path/to/user-api').then(response => {
      this.setState({users: response.data});
    });
  },

  render: function() {
    return <UserList users={this.state.users} />;
  }
});

export default UserListContainer;

It does its Ajax request and updates its own local state. But if other areas in the application need to change based on the newly acquired user list, this strategy won't suffice.

Example: connecting with react-redux

With the Redux strategy, we can dispatch an action when the Ajax request returns instead of doing this.setState(). Then this component and others can subscribe to the state change. But this actually brings us to a question of how do we setup the store.subscribe() to update the component's state? The "react-redux" module allows us to "connect" React components to Redux.



import React from 'react';
import { connect } from 'react-redux';
import store from '../path/to/store';
import axios from 'axios';
import UserList from '../views/list-user';

const UserListContainer = React.createClass({
  componentDidMount: function() {
    axios.get('/path/to/user-api').then(response => {
      store.dispatch({
        type: 'USER_LIST_SUCCESS',
        users: response.data
      });
    });
  },

  render: function() {
    // We no longer need getInitialState() to exist. Also notice that we refer to this.props.users instead of this.state.users
    return <UserList users={this.props.users} />;
  }
});

const mapStateToProps = function(store) {
  return {
    // The name "userState" came from when we combined our reducers
    users: store.userState.users
  };
}

// The first argument to connect() is a function that should return an object. The object's properties will become "props" on the component.
export default connect(mapStateToProps)(UserListContainer);

Example: Reducers


const initialUserState = {
  users: []
}

const userReducer = function(state = initialUserState, action) {
  switch(action.type) {
  case 'USER_LIST_SUCCESS':
    return Object.assign({}, state, { users: action.users });
  }
  return state;
}

Example: dispatching from events

react-redux helps in the cases where a UI event simply needs to dispatch an action.



const mapDispatchToProps = function(dispatch, ownProps) {
  return {
    toggleActive: function() {
      dispatch({ ... });
    }
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(UserListContainer);

// In the Presentation Component, we can do onClick={this.props.toggleActive} just as we did before but this time we didn't have to write the event itself.

Container Component Omission

The container js file could be shorted as:



import React from 'react';
import { connect } from 'react-redux';
import UserList from '../views/list-user';

const mapStateToProps = function(store) {
  return {
    users: store.userState.users
  };
}

export default connect(mapStateToProps)(UserList);
// The connect() creates a Container Component for us.

Example: provider



import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import router from './router';

ReactDOM.render(
  <Provider store={store}>{router}</Provider>,
  document.getElementById('root')
);

Containers and Presentational Components

medium.com: Presentational and Container Components.

Presentational components:

Container components:

Benefit: Better reusability. You can use the same presentational component with completely different state sources, and turn those into separate container components that can be further reused.

References

Component

reactjs.org: react component.

gitbooks.io is a very good in-depth on React.

Construction and initialization

This process includes: construction, default props, initial state.



//---------- For ES6 Class

import React from 'react';

class Person extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    return (
      <div>{ this.props.name } (age: { this.props.age })</div>
    );
  }
}

Person.defaultProps = { age: 'unknown' };

export default Person;

//---------- For createClass (ES6/ES5/CoffeeScript, etc.)

// For React.createClass Components, there is a helper method called getInitialState() which returns the state object.

var Person = React.createClass({
  getDefaultProps: function() {
    return ({ age: 'unknown' });
  },

  getInitialState: function() {
    return ({ count: 0 });
  },

  render: function() {
    return (
      <div>{ this.props.name } (age: { this.props.age })</div>
    );
  }
});

null vs. undefined props

When assigning default props, the React object merge code sees "null" as a defined value.



<Person name="Bob" age={ null } />

// Because "null" is a defined value our Component would render this as <div>Bob (age:)</div> instead of rendering "unknown".

Lifecycle

Mounting
  constructor()
  componentWillMount()
  render()
  componentDidMount()
Updating
  componentWillReceiveProps(nextProps)
  shouldComponentUpdate()
  componentWillUpdate(nextProps, nextState)
  render()
  componentDidUpdate()
Unmounting
  componentWillUnmount()
Error Handling
  componentDidCatch()

componentDidMount()

componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.

This method is a good place to set up any subscriptions. If you do that, don’t forget to unsubscribe in componentWillUnmount().

componentWillReceiveProps(nextProps)

We can get our current props by calling "this.props" and the new value is the "nextProps" argument passed to the method.

shouldComponentUpdate()

It allows your Component to exit the Update life cycle if there is no reason to apply a new render. Out of the box, the shouldComponentUpdate() is a no-op that returns true.

componentWillUpdate(nextProps, nextState)

componentWillUpdate() is called every time a re-render is required, such as when this.setState() is called.

This method is called before render(). Because we have not rendered yet, our Component's access to the Native UI (DOM, etc.) will reflect the old rendered UI.

To access the old props or state, we can call this.props or this.state.

We should not call this.setState() here because it triggers another componentWillUpdate(). If we trigger a state change in componentWillUpdate() we will end up in an infinite loop.

componentDidUpdate(prevProps, prevState)

The most common uses of componentDidUpdate() is managing 3rd party UI elements and interacting with the Native UI.

Re-render after update?

One of the worst things to do is do an unchecked setState(), we would fall into an infinite render loop.

If you need to do something like this, then you can implement a check at componentDidUpdate():



componentDidUpdate(prevProps, prevState) {
  // One possible fix...
  let height = ReactDOM.findDOMNode(this).offsetHeight;
  if (this.state.height !== height ) {
    this.setState({ internalHeight: height });
  }
}

React Router

medium.com: a simple tutorial by Paul Sherman.

github.com: react router dom - quick start.



import React from 'react'
import {
  BrowserRouter as Router,
  Route,
  Link
} from 'react-router-dom'

const Home = () => (
  <div>
    <h2>Home</h2>
  </div>
)

const About = () => (
  <div>
    <h2>About</h2>
  </div>
)

const Topic = ({ match }) => (
  <div>
    <h3>{match.params.topicId}</h3>
  </div>
)

const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <ul>
      <li>
        <Link to={`${match.url}/rendering`}>
          Rendering with React
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/components`}>
          Components
        </Link>
      </li>
      <li>
        <Link to={`${match.url}/props-v-state`}>
          Props v. State
        </Link>
      </li>
    </ul>

    <Route path={`${match.url}/:topicId`} component={Topic}/>
    <Route exact path={match.url} render={() => (
      <h3>Please select a topic.</h3>
    )}/>
  </div>
)

const BasicExample = () => (
  <Router>
    <div>
      <ul>
        <li><Link to="/">Home</Link></li>
        <li><Link to="/about">About</Link></li>
        <li><Link to="/topics">Topics</Link></li>
      </ul>

      <hr/>

      <Route exact path="/" component={Home}/>
      <Route path="/about" component={About}/>
      <Route path="/topics" component={Topics}/>
    </div>
  </Router>
)
export default BasicExample

PropTypes



import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};

PropTypes.[Type]

array
bool
func
number
object
string
symbol

node - Anything that can be rendered.
element - A React element.

instanceOf(ClassName) - Uses JS's instanceof operator.

oneOf(['News', 'Photos']) - enum
oneOfType([
    PropTypes.string,
    PropTypes.number,
    PropTypes.instanceOf(Message)
  ]),

arrayOf(PropTypes.number) - An array of a certain type.
objectOf(PropTypes.number) - An object with property values of a certain type.

shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
  }), - An object taking on a particular shape

// Chain any of the above with `isRequired` to make sure a warning is shown if the prop isn't provided.
requiredFunc: PropTypes.func.isRequired,
// A value of any data type
requiredAny: PropTypes.any.isRequired,

See this for good reference: github.io: typechecking with proptypes.

Default value



// Specifies the default values for props:
Greeting.defaultProps = {
  name: 'Stranger'
};

// The defaultProps will be used to ensure that this.props.name will have a value if it was not specified.

JSX

Syntax

facebook.github.io: DOM elements.

A list from HTML to JSX:

tabindex    tabIndex
class     className
innerHTML     dangerouslySetInnerHTML (pass an object with a __html key)

aria-*    (same)
data-*    (same)

"checked" is supported by "input" components of type "checkbox" or "radio". "defaultChecked" is the uncontrolled equivalent.



// "props.children" in MyComponent will simply be the string "Hello world!".
<MyComponent>Hello world!</MyComponent>

The "style" attribute accepts a JavaScript object with camelCased properties rather than a CSS string.



const divStyle = {
  color: 'blue',
  backgroundImage: 'url(' + imgUrl + ')',
  // Vendor prefixes other than ms should begin with a capital letter.
  WebkitTransition: 'all', // note the capital 'W' here
  msTransition: 'all' // 'ms' is the only lowercase vendor prefix
};

function ComponentWithTransition() {
  return <div style={divStyle}>This should work cross-browser</div>;
}

In depth

facebook.github.io: jsx in depth.

The JSX code:



<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

//compiles into:

React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

User-defined components must be capitalized, otherwise React thinks it is an HTML tag.

Escaped string literals



// Plain string is not escaped.
<MyComponent message="hello world" />

<MyComponent message="&lt;3" />
// Same as
<MyComponent message={'<3'} />

Using Dot Notation for JSX Type



import React from 'react';

// 'MyComponents' is a single module that exports many React components.
const MyComponents = {
  DatePicker: function DatePicker(props) {
    return <div>Imagine a {props.color} datepicker here.</div>;
  }
}

function BlueDatePicker() {
  return <MyComponents.DatePicker color="blue" />;
}

Choosing the type at Runtime



import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(props) {
  // Wrong! JSX type can't be an expression.
  // return <components[props.storyType] story={props.story} />;

  // Correct! JSX type can be a capitalized variable.
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}

FAQ

Comment in JSX

wesBos.com: react jsx comments.


{/* A JSX comment */}

{/*
  Multi
  line
  comment
*/}