The World of SPAs (& React)

Problems with the Internet

  • The web was originally set to work as documents that linked to each other
  • You've all seen a link: Home About Contact, etc.
  • When you click on them your browser throws away your current document display setup and starts from scratch to make a new one
  • But why? Because many parts of a web page stay the same (Headers, menus, sidebars, etc.) - so why destroy them every time? If only there was some way of keeping most of the page and just rebuilding the bits that changed?
  • JavaScript to the RESSCCUUUUUUUUEEE!!!!! Let's make an app that controls a single page!

What is an SPA?

  • An SPA is a Single Page Application
  • A single page loads synchronously from the server, but then all other operations are controlled by JavaScript
  • Any internal site navigations now happens asynchronously
  • Or, to put it another way, you load an initial page frame and use JavaScript to construct the different views
  • If you need more data to do so, then you call the server via AJAX.
  • The page can read the URL it is loaded with and render the relevant parts

Great! So, if JavaScript is in control then...

  • We can use JS to produce HTML
    • No more writing the same HTML on every page
      • Re-use of basic patterns (templating)
  • We can intelligently decide what content is shown
    • especially in response to user input
  • IF you've never thought about it, a website basically consists of data and a presentation layer (the HTML that surrounds it)
  • What if we watch our data and react if it changes?
    • Then we have a web app!
    • We can enhance this by watching other things in the environment, such as geolocation, to enhance the experience!

Reactive Programming

  • A part of most SPAs is that they use reactive programming
  • You've seen reactive (declarative) programming before in Excel spreadsheets, where you use a formula in a cell (e.g. =SUM(A3:A13);)
  • The opposite of reactive/declarative programming is what you've been doing, which is procedural/imperative programming.
    • In imperative you say: 'If this delete button is pressed then remove this part of the DOM'
    • In declarative programming you say: 'Watch this data - if it changes, re-render the list' (N.B. User actions can change the data...).

JavaScript SPA Frameworks

What SPA Frameworks are there?

  • React is Facebook's SPA framework
  • Angular is Google's
  • Vue.js is a community collaboration
  • Svelte
  • SOLID.js - NEW!
  • Other famous ones include:
    • Backbone (Old but still useful for models)
    • Ext.js (old)
    • Ember
    • Aurelia
    • Durandal
    • Can.js
    • etc.

What's good about React?

  • Mature Ecosystem
    • It has run on facebook for a decade +
  • Cross-platform (React Native, Electron, etc.)
  • Component-based architecture
    • Breaks the problem down
    • Makes work re-usable
  • Virtual DOM (faster)
  • Properties flow one-way between components, making things more predictable
  • JSX (write html-like code)

Docs

Components?

  • SPAs tend to have a way to create components, but why would you?
  • They promote code re-use
  • Components are can be 'dropped in' anywhere.
  • They are smaller and more readable than having a whole page, so it's easy to see what's going on
  • It allows the single responsibility principle - one file deals with one thing which has one purpose.

Components cont...

  • There are several types of components:
    • State-less
      • functions that take props (args) and print HTML
    • Stateful
      • Components that hold data that changes with user interaction
    • Higher Order Components (Later in the course)
      • Components that wrap other components to give them extended powers (like a theme-provider)
  • Components can be written in JSX (html-like templating language) e.g. <MyComponent />
  • Components, if you look carefully, are basically functions...

Writing with JSX

  • Much like HTML
  • Needs to be compiled!! (babel in codepen)
  • React, the package, must be in scope
  • Must be returned in () if more than one line
  • Can have children (both literal nodes and other components)
  • Must have a single root element
    • You can use fragments (see next slides)
  • HTML attributes are camel cased
  • Because it's JavaScript, some attributes, like `class` will be an issue (see next slides)
  • Placeholders use the {} syntax
    • these can hold any javascript
  • https://codepen.io/jmsherry/pen/yQwyxB

Sidenote: React.Fragment

  • When you render a react component it must have a single root element
  • Sometimes that's not optimal, so you can use <React.Fragment>
function MyComponent() {
	return (
    	<>
        	<h1>My Title</h1>
            	<p>.....</p>
        </>
    );
}
  • <React.Fragment>....</React.Fragment> is the full syntax
  • If they are used in iteration then they still need a key={} prop

JSX attributes and JS reserved words

  • JSX is great for producing HTML-like syntax but there is a problem
  • Imagine you write <p class="something">sjdkflsk</p> or <label for="email">Email</label>
  • The issue is that for and class are JavaScript reserved words: You're accidentally using JavaScript commands in a JS file whilst trying to write HTML/JSX.
  • Solution: Instead of class, write className, and; instead of for, write htmlFor
    • N.b. Any hyphenated attribute name in HTML is written in camelCase for JSX

Great! But that's static HTML, I could write that myself!

I thought this stuff was dynamic?

Well, it is...!!

Props

What are props?

  • Short for properties
  • props are like arguments
  • they are the information the component needs to run
  • They are things we currently know at render-time
  • In the first example we saw the name being passed into the component. This was a prop.
  • Props can be passed as:
    • literal values: name="James"
    • expressions/computed: dogAge={age*7}
      • You can execute any JS in the {}s
  • Props are (or should be!) READ ONLY
  • You can validate them
  • You can use es6 default params to provide defaults

https://codepen.io/jmsherry/pen/oQVgmG?editors=0010

Handling Events

  • To add events use an html attribute camelCased, e.g.: onClick
class MyApp extends React.Component {
  
  // Function we can pass straight in
  // or one which gets called by react
  handler = (event) => {
    event.persist();
    console.log(event.target.value);
    // .....
  };

  // A function we need to pass args to
  // when we call it
  log = (num) => {
    console.log(num)
  };


  render() {
    console.log('rendering');
    return (
      <>
        <h1>Search</h1>
        <input onChange={this.handler} />
	    <button onClick={() => {
            this.log(2)
        }}>Click me</button>
      </>
	);
  }
}

ReactDOM.render(<MyApp />, document.getElementById('mount'));
  • You then bind a function  (see above for passing args) (demo)
  • Event objects are synthetic (if you pass to something async, like setState or a timer,  you'll need to...)
    • event.persist() (demo)

OK, so we pass stuff to components and the are written into the page: What happens then? How do they 'react'?

State

What is state?

  • State is an in-memory model of the data that component uses, which can be changed to cause the component to react
  • It is internal and private to that particular component
  • It is designed to be changed during the time the component is in the page
  • State is constructed in the constructor and destroyed when the component is unmounted
    • Problem there: Every time we unmount we lose any data the user had put in! (answer: State management, e.g. redux)
  • What you will tend to do is pass parts of the state as props to child components, so when state changes they re-render!
  • You programatically change state with setState()
  • To use state you used to have to create a class component

To re-iterate:

Props vs. State

  • Props are values we know and can calculate in advance
    • They allow the component to be initially drawn/rendered
       
  • State is values held inside the component which are subject to change after the component has been rendered (usually through async/user interaction)

So how do I change the state then?

setState?

  • setState is a method available in class instances (because they extend Component)
  • It allows you to programatically change state, which causes the application to update
  • It is not available in various lifecycle methods to prevent repeated re-rendering being triggered
  • It is async
  • It does have a callback
  • It has 2 method signatures
  • Docs
  • DEMO
  • When/where can I call it?
    • In event handler functions and some lifecycle methods.

Recap

  • We can make components
  • We can make them share data
  • We know how they respond dynamically to changes in that data

 

But how do we set up that data; make intelligent decisions about it, or; decide what's drawn?

Component LifeCycle

So we can interrupt and supervise the process...

Component LifeCycle

The bits in the blue boxes are method names you can use to override the default behaviour and put your own custom code in. 

Birth

constructor()

A place to:

  • Declare initial state
  • bind functions to the instance
  • NO LONGER NEEDED UNLESS PASSING DYNAMIC INFORMATION
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {items: [], text: ''};
        this.someMethod = this.someMethod.bind(this);
   }
// ...
}

componentWillMount()

  • A place to sort out prior formatting of necessary information.
  • YOU ARE NOT IN THE DOM YET!!
  • This method happens before initial rendering and is the last place you can manipulate data before then.
  • changing things here doesn't trigger a re-render
  • It is the only method that is called if you render your react server-side with isomorphic rendering...
  • THIS METHOD IS DEPRECATED AND WILL BE REMOVED IN v.17
class TodoApp extends React.Component {
    constructor(props) {
    super(props);
    this.state = { mode: null } ;
  }

  componentWillMount() {
    let mode;
    if (this.props.age > 70) {
      mode = 'old';
    } else if (this.props.age < 18) {
      mode = 'young';
    } else {
      mode = 'middle';
    }
    this.setState({ mode });
  }
  // ... after CWM/CDM and before render
  someMethod() {
    doSomething();
  }
}

render()

  • Probably the most important method
  • Here you return JSX (which is parsed into HTML)
  • This is the bit that determines what shows up when your component is run
  • This is where you do your templating
  • Cannot this.setState() here
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {items: [], text: ''};
        this.someMethod = this.someMethod.bind(this);
   }
// ...
    someMethod() {
        doSomething();
    }
    render() {
      // Fragments can be expressed as <>....</> aswell
        return (
            <>
                <h1 className="main-title">{this.props.title}</h1>
                <button onClick={this.someMethod}>Do something</button>
            </>
        );
    }
}

componentDidMount()

  • Now you are in the DOM
  • This is the place for AJAX calls, timers, etc.
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {items: [], text: '', cars: []};
        this.someMethod = this.someMethod.bind(this);
   }
// ...
    componentDidMount() {
        // Axios is an AJAX client. ('fetch' is another, but isomorphic!)
        axios.get('/cars')
        .then((resp) => {
            this.setState({cars: resp});
        })
        .catch((err) => {
            console.log('Error', err.message);
        });
    }
// ...
}

Life

Receiving new props

componentWillReceiveProps()

  • This is the first hook that allows us to look into the upcoming Update.
  • Here we could extract the new props and update our internal state.
  • If we have a state that is a calculation of multiple props, we can safely apply the logic here and store the result using this.setState();
  • Skipped if props haven't changed
  • Arguments passed:
    • nextProps
    • nextContext
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = { suspended: false };
        this.someMethod = this.someMethod.bind(this);
   }
// ...
    componentWillReceiveProps(newProps, newContext) {
        if (newProps.suspended) {
           this.setState({ suspended: true });
        }
    }
}

Receiving new state

shouldComponentUpdate()

  • Is more or less a question for you to answer
  • You are passed the (nextProps, nextState, nextContext)
  • You make a decision based on them
  • If you return true, the component updates
  • If you return false it does not
  • This is a good way to head off unnecessary re-renders
  • Cannot this.setState() here
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {items: [], text: ''};
        this.someMethod = this.someMethod.bind(this);
    }
    shouldComponentUpdate(newProps, newState) {
        if (newProps.suspended) {
            return false;
        }
        return true;
    }
// ...
}

componentWillUpdate()

  • Any access to the DOM here is OLD DOM
  • You are passed nextState,  nextProps and nextContext as arguments
  • Analogous to componentWillMount()
  • Last stop before render is run again with new state & props
  • Cannot this.setState() here
  • THIS METHOD IS DEPRECATED AND WILL BE REMOVED IN v.17
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {items: [], text: ''};
        this.someMethod = this.someMethod.bind(this);
   }
   componentWillUpdate(newProps, newState, newContext) {
    if (newState.low) {
        newProps.units +=1;
    }
   }
// ...
}

componentDidUpdate()

  • Analogous to componentDidMount()
  • Your component is now in the DOM and AJAX, Timers, etc. should be called now
  • You are passed the props, state and context from before the update - this is your last chance to access them
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {items: [], text: ''};
        this.someMethod = this.someMethod.bind(this);
   }
    componentDidUpdate(prevProps, prevState) {
      // only update chart if the data has changed
      if (prevProps.data !== this.props.data) {
        this.chart = c3.load({
          data: this.props.data
        });
      }
    }
// ...
}

Death

componentWillUnmount()

  • The component is removed from the DOM
  • state will be destroyed
  • last chance to:
    • send signals to other parts of the app
    • persist state to storage
    • CLEAR TIMERS, ETC.
  • This is really about clearing up for memory management
  • Cannot this.setState() here
class TodoApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = { items: [], text: '', interval: null };
        this.someMethod = this.someMethod.bind(this);
    }

    componentDidMount() {
        const timer = setInterval(function(){...}, 3000);
        this.setState({ interval: timer });
    }
// ...
    componentWillUnmount() {
        clearInterval(this.state.interval);
    }
}

Guide to LifeCycle

VS_Code Plugin

Styling

Styling

  • Against everything you've been taught, styling is placed directly in the style attribute like so:
class MyComponent extends React.Component {
    render() {
        const headerStyles = {
            backgroundColor: 'red',
            zIndex: 99999
        };
        return (
            <h1 style={headerStyles}>{this.props.heading}</h1>
        );
    }
}

// OR

function MyComponent(props){
  return (
    <h1 style={{
    	color: '#f00'
    }}>{props.head}</h1>
  );
}
  • It is done as a POJO (plain old javascript object)
  • hyphenated properties are camelCased
  • Libs: Material UI

CSS Modules

  • Link
  • Can't use in codepen (requires build process)
.container {
  max-width: 1280px;
  margin: auto;
}
import { container } from './styles.css';

function MyComponent(){
  return (
    <h1 className={container}>Hello!</h1>
  );
}

styles.css

MyComponent.js

Often used with the classnames package for multiple, dynamic classes

Styled Components!!!!!