INF310: Modern JS, Fall 2017 > Frameworks and Libraries > React
React
  • declarative syntax
  • one-way data flow
  • virtual dom
  • atomic design
Simple Components

A component is a function that returns an element.

  function Hello(props) {
    return React.createElement(
      'div', null, `Hello, ${props.name}`
    );
  }

  function App() {
    return React.createElement('div', null, [
      React.createElement(Hello, { name: 'everyone' })
    ]);
  }
JSX

html-like syntax sugar that transpiles to React.createElement calls.

  // always import React when using JSX
  import React from 'react';

  const HelloWorld = () => (<div>Hello World!</div>);

  const Hello = (props = { name: 'stranger'}) => (
    <div>Hello {props.name}</div>
  );

  const Page = () => (
    <div>
      <HelloWorld />
      <Hello name="everyone" />
    </div>
  );
Atomic Design

Composing with shared UI primitives

From atoms to molecules to organisms and beyond

Stateful components

Sometimes components must remember things.

  class SimpleForm extends Component {
    constructor(props) {
      super(props);
      this.state = { input: '' };

      this.getInput = event => {
        const input = extactInput(event);
        this.setState({ input });
      };
    }

    render() {
      return (
        <div>
          {this.props.title}
          <input
            onChange={this.getInput}
            value={this.state.input}
          />
          <button>Submit</button>
        </div>
      );
    }
  }
Component Lifecycle

componentDidMount: Start loading data after the component has been rendered once.

  class ListWithData extends Component {
    constructor(props) {
      super(props);
      this.state = { items: [] };
    }

    componentDidMount() {
      fetch('/list')
        .then(data => this.setState({ items: data }));
    }

    render() {
      <ul>
        {this.state.items.map(item => (
          <li>{item.title}</li>))}
      </ul>
    }
  }
Lifecycle methods

Each component goes through the lifecycle loops.

React lifecycle

Unidirectional data flow

Receive action -> Update state -> Update UI

The flux architecture

Rendering
  // in the browser
  ReactDOM.render(
    <App />,
    document.getElementById('app')
  );

  // on the server
  ReactDOMServer.renderToString(
    React.createElement(App, props);
  );
Virtual DOM
  • diffing
  • reconciling
  • minimal DOM manipulation
Virtual DOM

Diffing and reconciling

State Management
  • conserns:
    • side-effects
    • async
  • solutions:
    • callbacks
    • containers
    • stores
Callbacks
    // SimpleForm component
    render() {
      return (
        <div>
          {this.props.title}
          <input onChange={this.getInput} />
          <button onClick={
            () => this.props.onSubmit(this.state.input)}>
            Submit
          </button>
        </div>
      );
    }

    // in another stateful component
    <SimpleForm
      onSubmit={input => this.setState({ name: input })} />
    <SimpleForm
      onSubmit={input => this.setState({ age: input })} />
Container components
  class LoadingContainer extends Component {
    constructor(props) {
      super(props);
      this.state = { loading: true };
    }

    componentDidMount() {
      this.props.getData().then(
        data => this.setState({ loading: false, data }));
    }

    render() {
      return this.state.loading
        ? this.props.loading
        : this.props.dataview(this.state.data);
    }
  }

  <LoadingContainer
    loading={<div>Loading, please wait</div>}
    dataview={data => <Table data={data} />}
    getData={() => fetch('/data.json')} />
Stores

Simple mutable store:

Note: this is a mock, don't use this in production

  const state = {};
  const subscribers = {};
  const dispatch = (action, newState) => {
    Object.assign(state, newState);
    subscribers[action].forEach(cb => cb(state));
  };
  const subscribe = (action, callback) {
    if (!subscribers[action]) {
      subscribers[action] = [];
    }
    subscribers[action].push(callback);
  };

  export default { subscribe, dispatch };
Using the store
  // Form Component
  <input
    onChange={(text) => store.dispatch('text', { text })}
  />

  // Decoupled Validation Component
  store.subscribe('text', ({text}) =>
    this.setState(validate(text) ? <Ok /> : <Error />)
  );

  // AutoSuggest service
  store.subscribe('text', ({text}) =>
    fetch(`/suggest?text=${text}`)
      .then(suggestions =>
        store.dispatch('suggestions', { suggestions })
      );
  )
Redux
  • single store
  • immuatable state
  • actions and reducers
  • react-redux
Other Front-end tools
  • Angular
  • Polymer
  • Vue
Links