Over the hype and under the hood

React wat

  • "A JavaScript library for building user interfaces" - reactjs.org
  • Emphasis on 'library'
  • Can be run both server side and client side
  • Component tree of re-usable components

  • Just Javascript = fast learning curve

👶 2012

🔓 2013

 📱  2015

🏃 2017

What went down

Need to know

  • state object
  • props object
  • render function
  • (lifecycle functions)

An entire app I

  • What you need
  • Initialise your project
  •  
  • Install the required dependencies
  •  
  • Create the source files
$ npm init -y
$ npm install --save react react-dom
$ mkdir src
$ touch src/index.html
$ touch src/App.js
$ yarn init -y
$ yarn add react react-dom

or

or

An entire app II

<!DOCTYPE html>
<html>
<head>
  <title>FluffyReact</title>
</head>
<body>
  <div id="my-app-root"></div>
  <script src="App.js"></script>
</body>
</html>
import React from 'react';
import ReactDom from 'react-dom';

class App extends React.Component {
  render() {
    return <h1>It's so fluffy!!!</h1>;
  }
}

ReactDom.render(
    <App />,
    document.getElementById('my-app-root')
);

index.html

   x

import React from 'react';
import ReactDom from 'react-dom';

class App extends React.Component {
  render() {
    return React.createElement('h1', null, 'It\'s so fluffy!!!');
  }
}

ReactDom.render(
    React.createElement(App, null, null),
    document.getElementById('my-app-root')
);

App.js

<!DOCTYPE html>
<html>
<head>
  <title>FluffyReact</title>
</head>
<body>
  <div id="my-app-root"></div>
  <script src="App.jsx"></script>
</body>
</html>

An entire app III

import React from 'react';
import ReactDom from 'react-dom';

class App extends React.Component {
  render() {
    return <h1>It's so fluffy!!!</h1>;
  }
}

ReactDom.render(<App />, document.getElementById('my-app-root'));
  • We've got a bunch of stuff that most browsers don't understand just yet e.g Module imports, Classes and JSX
  • We need to transpile this so it can run in most browsers

App.js

An entire app IV

$ parcel src/index.html
  • "Blazing fast, zero configuration web application bundler" - parceljs.org
  •  
  • Out the box babel transpilation and hot module resolution with zero configuration
    •  
  • That's it! Your app should be available through your browser at localhost:1234
$ npm install -g parcel-bundler
$ yarn global add parcel-bundler

or

Component anatomy:

case study

  • Meet Sipho, he loves fluff
  • He knows how much fluff he
    owns
  • He knows how to make more fluff
  • He gets told what colour fluff
    can make
  • He makes fluff when we tell
    him to

Todo app

import React from 'react';

class Sipho extends React.Component {
 constructor(props) {
   super(props);
   this.gimmeFluff = this.gimmeFluff.bind(this);

   this.state = { fluffs: 0 };               // knows how much fluff he owns
 }

 gimmeFluff() {                              // knows how to make more fluff
   this.setState({ fluffs: this.state.fluffs + 1 });
 }

 render() {
   return (
     <div>
       <h1>
         Sipho has {this.state.fluffs}
         {this.props.colour} fluff(s)        {/* gets told what colour fluff can make */}
       </h1>
       <button onClick={this.gimmeFluff}>    {/* makes fluff when we tell him to */}
         Generate Fluffs
       </button>
     </div>
   );
 }
}

export default Sipho;

Sipho.jsx

render() {
  return <Sipho colour="purple" />;
}
  render() {
    return <h1>It's so fluffy!!!</h1>;
  }
  • Meet Palesa, she’s an enabler
  • She knows how to make fluff, but only for Sipho
  • She makes fluff when we tell her to
  • She knows how much fluff Sipho has
  • She knows how much fluff she’s given to Sipho

Component anatomy:

even more fluff

  • Problem:
    • We need to share state between components
    • We need to update a sibling component’s state
  • Solution:
    • Move the state into the parent component
    • Let the parent component control state changes

Component data flow

import React from 'react';
import ReactDom from 'react-dom';

import Sipho from './Sipho';
import Palesa from './Palesa';

class App extends React.Component {
  constructor (props) {
    super(props);
    this.giveSiphoFluff = this.giveSiphoFluff.bind(this);

    this.state = { siphosFluffs: 0 };
  }

  giveSiphoFluff() {
    this.setState({ siphosFluffs: this.state.siphosFluffs + 1 });
  }

  render () {
    return (
      <div>
        <Sipho
          colour="purple"
          fluffs={this.state.siphosFluffs}
          gimmeFluff={this.giveSiphoFluff}
        />
        <hr />
        <Palesa
          siphosFluffs={this.state.siphosFluffs}
          giveSiphoFluff={this.giveSiphoFluff}
        />
      </div>
    );
  }
}

ReactDom.render(<App />, document.getElementById('my-app-root'));

App.jsx

import React from 'react';

class Palesa extends React.Component {
  constructor(props) {
    super(props);
    this.state = { charityFluffs: 0 };         // knows how much fluff she’s given to Sipho

    this.giveFluff = this.giveFluff.bind(this);
  }

  giveFluff() {                                // knows how to make fluff for Sipho
    this.setState({ charityFluffs: this.state.charityFluffs + 1 }, () => {
      this.props.giveSiphoFluff();
    });
  }

  render() {
    return (
      <div>
        <h1>
          {/* knows how much fluff Sipho has */}
          Palesa's contributed {this.state.charityFluffs} fluff(s)
          towards Sipho's {this.props.siphosFluffs} fluff(s)
        </h1>
        <button onClick={this.giveFluff}>
          Generate Fluff For Sipho!
        </button>
      </div>
    );
  }
}
export default Palesa;

Palesa.jsx

import React from 'react';

const Sipho = ({ colour, fluffs, gimmeFluff }) => (
  <div>
    <h1>Sipho has {fluffs} {colour} fluff(s)</h1>
    <button onClick={gimmeFluff}>Generate Fluff!</button>
  </div>
);

export default Sipho;

Sipho.jsx

import React from 'react';

function Sipho(props) {
  return (
    <div>
      <h1>Sipho has {props.fluffs} {props.colour} fluff(s)</h1>
      <button onClick={props.gimmeFluff}>Generate Fluff!</button>
    </div>
  )
};

export default Sipho;
import React from 'react';

class Sipho extends React.Component {
  render() {
    return (
      <div>
        <h1>Sipho has {this.props.fluffs} {this.props.colour} fluff(s)</h1>
        <button onClick={this.props.gimmeFluff}>Generate Fluff!</button>
      </div>
    );
  }
}

export default Sipho;

A stateful catastrophe

  • Internal React state works beautifully for simple apps
  • Gets messy with complex applications, that have state changes in different places, that may be asynchronous, and dependent on other models
  • What started off as a neat dependency tree can end up being spaghetti

Unidirectional data flow

  • We need a single source of truth! As well as a controlled way of updating it

 

 

 

 

 

  • React grew up side by side with an architectural pattern aimed at facilitating this paradigm

  • Flux is not a tool, its a pattern

  • Many libraries and helpers exist that help you adopt Flux in your application

A realisation of the principle

  • Redux is a state management library influenced by Flux principles
  • There exists only one application store, and each sub-domain is a slice of that store
  • A source so true, it's immutable (store)
  • The read-only state can only be mutated emitting an event  specifying an intent to update the state (action)
  • Pure functions handle these actions to handle state mutations (reducer)
$ npm install --save redux react-redux
$ yarn add redux react-redux

or

Redux

View 👉 Action 👉 Reducer 👉 Store 👉 View

 

 

  • Provides a store object with the following API:
    • dispatch ()  - trigger a store change
    • subscribe() - respond to store changes
    • getState()    - read the store's data

Redux-managed fluff:

Reducer

const initialState = {
  siphosFluffs: 0,
  charityFluffs: 0
};

export function fluffs(state = initialState, action) {
  switch (action.type) {
    case 'GIMME_FLUFF':
      return {
        ...state,
        siphosFluffs: state.siphosFluffs + 1
      };

    case 'GIVE_SIPHO_FLUFF':
      return {
        ...state,
        siphosFluffs: state.siphosFluffs + 1,
        charityFluffs: state.charityFluffs + 1
      };

    default:
      return state;
  }
}

fluffs.js

Redux-managed fluff:

Actions

export function giveSiphoFluff() {
  return { type: 'GIVE_SIPHO_FLUFF' };
}

export function gimmeFluff() {
  return { type: 'GIMME_FLUFF' };
}

actions.js

Pause, HOC

  • We need to be able to connect our component to the dispatcher as well as the store
  • We could manually tap into dispatch(), subscribe() and getState() API
  • Rather use optimised React-Redux bindings:

connect()

  • Higher order component
  • Accepts our function as an argument

  • Mutates our props to include our actions and state from the store

<Provide />

  • Just a component
  • Makes the store available to our components
import React from 'react';
import ReactDom from 'react-dom';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
import * as actions from './actions';
import { fluffs } from './reducer';
import Sipho from './Sipho';
import Palesa from './Palesa';

const App = ({ siphosFluffs, charityFluffs, gimmeFluff, giveSiphoFluff }) => (
  <div>
    <Sipho
      colour="purple"
      fluffs={siphosFluffs}
      gimmeFluff={gimmeFluff}
    />
    <hr />
    <Palesa
      siphosFluffs={siphosFluffs}
      charityFluffs={charityFluffs}
      giveFluff={giveSiphoFluff}
    />
  </div>
);

const mapStateToProps = state => ({
  siphosFluffs: fluffs(state, {}).siphosFluffs,
  charityFluffs: fluffs(state, {}).charityFluffs
});
const mapDispatchToProps = dispatch => ({
  giveSiphoFluff: () => dispatch(actions.giveSiphoFluff()),
  gimmeFluff: () => dispatch(actions.gimmeFluff())
});
const store = createStore(fluffs);

const AppContainer = connect(mapStateToProps, mapDispatchToProps)(App);

ReactDom.render(
  <Provider store={store}>
    <AppContainer />
  </Provider>,
  document.getElementById('my-app-root')
);

App.jsx

import React from 'react';

const Sipho = ({ colour, fluffs, gimmeFluff }) => (
  <div>
    <h1>Sipho has {fluffs} {colour} fluff(s)</h1>
    <button onClick={gimmeFluff}>Generate Fluff!</button>
  </div>
);

export default Sipho;

Sipho.jsx

import React from 'react';

const Palesa = ({ charityFluffs, siphosFluffs, giveFluff }) => (
  <div>
    <h1>
      Palesa's contributed {charityFluffs} fluff(s)
      towards Sipho's {siphosFluffs} fluff(s)
    </h1>
    <button onClick={giveFluff}>
      Generate Fluff For Sipho!
    </button>
  </div>
);

export default Palesa;

Palesa.jsx

But Why?

  • Cleaner views
  • Testable code
  • Debug-able state changes
  • Linear state dependency tree
  • Smoother scaling (your app, and the team)
  • Only if you need it, though

Questions?

GitHub: github.com/ntkzwane

Feedback/suggestions: ntkzwane@gmail.com

ZATech Slack: @ntokozo

ReactJS: Over the hype and under the hood

By Ntokozo Zwane

ReactJS: Over the hype and under the hood

  • 159