React for Redux

Agenda

  1. Setting up the environment
  2. Modern Javascript
  3. React
  4. Redux
  5. Tests

Setting up the environment

NPM

npm is the package manager for node.js, the server-side JavaScript execution environment. Most React apps load the React library and 3rd party libraries/extensions through npm packages.

If you're new to JavaScript development, or if you've been using older libraries (e.g. jQuery, Backbone), you may not have used npm for client-side app development. While npm was originally intended for use​ exclusively in node.js server-side code, it's now commonly used for the client-side code too. (Webpack)

Useful commands

  • npm init –f

  • npm install –save (npm i -S)

  • npm install –save-dev (npm i -D)

  • npm uninstall (npm un)

  • npm start (npm start -- --env=dev)

  • npm test (npm t; npm t -- --watch)

Webpack

Webpack bundles your client-side code (JavaScript, CSS, etc) into a single JavaScript file. Webpack is highly configurable with plugins, allowing you to bundle nearly any kind of asset imaginable.

Webpack is the most common way to bundle a React app during development and for production. If you want more flexibility and power than Facebook's create-react-app, you should learn how to use Webpack and the Babel plugin (for compiling your ES2015/JSX code for browser compatibility)

Webpack is a huge topic, and the official documentation is work-in-progress. You'll likely run into issues and have to search for relevant stack overflow answers and git issues. For now, this is pretty normal, so expect to do some troubleshooting and don't get too discouraged!​

Useful Commands

  • npm install webpack –D

  • npm install webpack-dev-server –D

module.exports = env => {
    return {
        entry: “index.js”,
        output: {
            filename: “bundle.js”
        },
        plugins: [
            CommonsChunkPlugin, ExtractTextPlugin, 
            CleanWebpackPlugin, HtmlWebpackPlugin, DefinePlugin
        ]
    };
};

Babel

Babel is a highly configurable compiler that lets you use experimental JavaScript features and extensions, compiling down into older JavaScript versions that can be supported on a wider range of platforms. Of course, if a native platform doesn't support an ES2015 feature like Promise(), Babel won't fully be able to help -- but it can in many cases "polyfill" missing APIs to provide this functionality.

Babel enables debugging of the original source code by including source maps with the compiled JavaScript. JavaScript interpreters will run the compiled code, but map it to the source code in the debugger so that you can debug the source code instead of the (generally quite ugly) compiled output.

Plugins, presets and stages

Babel comes in two parts: the core, and plugins. Each individual language feature that Babel can compile, such as ES2015 classes, has a separate plugin. Collections of plugins are grouped into presets, so that you don't have to install hundreds of individual dependencies.

Babel groups experimental language features into presets called stages, with stage-0 being the most experimental (i.e. these may not make it into the official language spec) and stage-3 (these features aren't going anywhere).

 

  • Stage 0  - Strawman: just an idea, possible Babel plugin.
  • Stage 1  - Proposal: this is worth working on.
  • Stage 2  - Draft: initial spec.
  • Stage 3  - Candidate: complete spec and initial browser implementations.
  • Stage 4 - Finished: will be added to the next yearly release.

 

Babel Presets

Useful Commands

npm install --save-dev babel-loader babel-core babel-preset-react babel-preset-env babel-preset-stage-1 babel-plugin-transform-runtime

 

npm install --save babel-runtime

module.exports = env => {
  return {
    entry: './index.js',
    output: {
      filename: 'bundle.js',
    },
    module: {
      rules: [
        {
          test: /.js$/,
          exclude: /node_modules/,
          use: [
            {
              loader: 'babel-loader',
              options: {
                cacheDirectory: true,
              },
            },
          ],
        },
      ],
    },
  }
}

Webpack Configuration

{
  "presets": [
    ["env", {"modules": false}],
    "stage-1",
    "react"
  ],
  "plugins": [
    "transform-runtime"
  ]
}

.babelrc

React

React comes in two parts, React and ReactDOM. React includes the core component APIs and rendering logic. ReactDOM contains the necessary APIs to render to the browser DOM.

 

npm install --save react react-dom

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
const styles = {
  app: {
    paddingTop: 40,
    textAlign: 'center',
  },
}
class App extends Component {
  render() {
    return (
      <div style={styles.app}>
        <h1>Welcome to React!</h1>
        <p>I'm a paragraph</p>
      </div>
    )
  }
}
const root = document.querySelector('#app')
ReactDOM.render(<App />, root)

Eslint

Eslint is a pluggable linting utility for Javascript.

 

Linting is a good coding practice whose goal is to detect possible errors and suspicious code, optimizing readability and maintainability, and enhancing your development with best practices for any given development paradigm (React, functional programming)

Useful Eslint Plugins

  • eslint-react-plugin
  • eslint-import-plugin
  • eslint-fp-plugin
  • eslint-promise-plugin
  • eslint-plugin-prettier

Useful Eslint commands

  • eslint **/src/**.js
  • --fix
  • --cache

Modern Javascript

ES6 Features

  • Block Scoped Declarations (let, const)
  • Fat Arrow Functions () =>{}
  • Destructuring const {one, two} = this;
  • Import and exports import * from “package”;
  • Default parameters const myFunction = (arg= “default”)=>{};
  • Classes class MyClass extends AnotherClass{}
  • Dynamic object keys const style = {[attribute]:value}
  • Array Spread const foo = [1,2,3,4]; console.log(…foo);
  • Static class properties class Foo { static bar=“hello”} console.log(Foo.bar);

 

http://kangax.github.io/compat-table/es6/​

ESNext

  • Class instance properties
  • Bound instance method no more bind!

  • Object spread const a={foo:"bar"}; let b ={{joe:"test"}, …a}

  • Object rest let {a, ...rest}

  • Async and await

  • Class and property decorators

  • Observable

  • See more https://kangax.github.io/compat-table/esnext/

React

Create-react-app

Facebook provides a command-line utility called create-react-app which automatically sets up a new React project with a sensible default project structure and feature set. This is the best way to get started as a beginner.

You'll likely outgrow this option pretty quickly as you get a better grasp of React and want to customize your stack. Fortunately, create-react-app offers an eject option to export your app, so you're not locked in.

React API

  • ReactDOM API

    • ReactDOM.render()

  • REACT API

    • React.Component

    • React.PureComponent

    • PropTypes

    • Children

    • cloneElement
    • context
this.setState({},()=>{console.log("callback")});
//receives a callback function

this.setState({state.quantity+1});
this.setState({state.quantity+1});
//updates once

this.setState((prevState) => ({counter: prevState.quantity + 1}));
this.setState((prevState) => ({counter: prevState.quantity + 1}));
//using and updater function, updates twice

this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});
// the updater function receives a second parameter, with the current props

setState()

Lifecycle API

class MyComponent extends React.Component{
    constructor(props){}
    componentWillMount(){}
    render(){}
    componentDidMount(){}

    componentWillReceiveProps(nextProps){}
    shouldComponentUpdate(nextProps, nextState){}
    componentWillUpdate(nextProps, nextState){}
    componentDidUpdate(prepProps, prevState){}
    componentWillUnmount(){}

}

Styling in react

  1. ordinary CSS, less, sass files.
  2. CSS-modules
  3. styled-components.
import React, { Component } from 'react'
import { render } from 'react-dom'

const randomColor = () => '#' + Math.random().toString(16).substr(-6);
class Card extends Component {
  render() {
    const style = {
      padding: 20,
      textAlign: 'center',
      color: 'white',
      backgroundColor: this.props.color,
    }

    return (
      <div style={style}>
        {this.props.children}
      </div>
    )
  }
}

class App extends Component {
  state = {
    color: 'skyblue'
  }

  randomizeColor = () => this.setState({color: randomColor()})
  render() {
    const {color} = this.state

const style = {
      padding: 20,
    }
    return (
      <div style={style}>
        <Card color={color}>
          <input type={'button'} value={'Randomize Color'}
            onClick={this.randomizeColor}
          />
        </Card>
      </div>
    )
  }
}

render(<App />, document.querySelector('#app'))
import React, { Component } from 'react'
import { render } from 'react-dom'
import styled from 'styled-components'

const randomColor = () => '#' + Math.random().toString(16).substr(-6);

const Card = styled.div`
  padding: 20px;
  text-align: center;
  color: white;
  background-color: ${props => props.color};

const Container = styled.div`
  padding: 20px;

class App extends Component {
  state = {color: 'skyblue'};
  randomizeColor = () => this.setState({color: randomColor()});
  render() {
    const {color} = this.state;
    return (
      <Container>
        <Card color={color}>
          <input type={'button'} value={'Randomize Color'}
            onClick={this.randomizeColor}
          />
        </Card>
      </Container>
    )
  }
};

render(<App />, document.querySelector('#app'))

React Best Practices

  • Performance

    • onClick={()=>this.setState({count:count+1})}

  • Input handling (controlled vs uncontrolled inputs)

  • Conditional rendering

    • if() == {myvar && <Component/>}

  • Ternary operator

    • {listHasItems ? <ComponentList/> : <EmptyList/>}

  • Keys

    • ​​articleList.map((article, index)=>(<li key={index}>{article}</li>))

  • Refs

  • Smart/Container Components & Presentational/Dumb Components

  • 5 approaches to binding method​​

Redux

Redux is a predictable state container for JavaScript apps.

 

  1. Describe application state as plain objects and arrays.

  2. Describe changes in the system as plain objects.

  3. Describe the logic for handling changes as pure functions.

  •  

Redux is that is an alternative way to manage state in a React.

class NormalReact extends React.Component {
 constructor() {
   super();
   this.state = {message: "This is a local state."};
 }
 componentDidMount(){
    this.setState({message:"state is changed when component is mounted"});
  }
}

Tradeoffs

  1. Describe application state as plain objects and arrays.

  2. Describe changes in the system as plain objects.

  3. Describe the logic for handling changes as pure functions.

Trade-in

Three principles

  1. Single source of truth

    • The state of your whole application is stored in an object tree within a single store.

  2. State is read-only

    • The only way to change the state is to emit an action, an object describing what happened.

  3. Changes are made with pure functions

    • To specify how the state tree is transformed by actions, you write pure reducers.

Redux Lifecycle

Sample State

// State
{
  todos: [{
    text: 'Eat food',
    completed: true
  }, {
    text: 'Exercise',
    completed: false
  }],
  visibilityFilter: 'SHOW_COMPLETED'
}


// Actions
const addTodoType = { type: 'ADD_TODO', text: 'Go to swimming pool' };
const toggleTodo = { type: 'TOGGLE_TODO', index: 1 };
const changeVisibility = { type: 'SET_VISIBILITY_FILTER', filter: 'SHOW_ALL' };

//Action creator
function addTodo(text){
    return {
        type:"ADD_TODO",
        text // same as text:text
    };
}

Sample Actions

Sample Reducer

function visibilityFilter(state = 'SHOW_ALL', action) {
  if (action.type === 'SET_VISIBILITY_FILTER') {
    return action.filter
  } else {
    return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return state.concat([{ text: action.text, completed: false }])
    case 'TOGGLE_TODO':
      return state.map(
        (todo, index) =>
          action.index === index
            ? { text: todo.text, completed: !todo.completed }
            : todo
      )
    default:
      return state
  }
}

Actions

An action is a plain JavaScript object that describes what happened. Actions must have a type property that indicates the type of action being performed. Types should typically be defined as string constants.

Actions are payloads of information that send data from your application to your store. They are the only source of information for the store. You send them to the store using store.dispatch()

 

Action creators

Action creators are exactly that—functions that create actions. It's easy to conflate the terms “action” and “action creator,” so do your best to use the proper term.

In Redux action creators simply return an action:

export default function addTodo(text) {
 return {
   type: ADD_TODO,
   text // same as text:text
 }
}
const text = "learn Redux";
store.dispatch(addTodo(text));

//within a container
dispatch(addTodo(text));

Then, we pass the action creator’s result to the store’s dispatch method:

Reducer

A reducer is a function that takes state and action as arguments​ and returns the next state of the app. It would be hard to write such a function for a big app, so we write smaller functions managing parts of the state

State

  1. The Store is the object that brings actions and reducers together. The store has the following responsibilities:

 

  1. Holds the application state;

  2. Allows access to state via getState();

  3. Allows state to be updated via dispatch(action);

  4. Registers listeners via subscribe(listener);

  5. Handles unregistering of listeners via the function returned by the function subscribe (listener).

 

Redux's Data Flow

  1. In Redux, the data flow is unidirectional:

  2. Dispatch an action to tell the store what happened

    • store.dispatch(action);

  3. The Redux store calls the reducer function you gave it.

    • let nextState = reducer(previousState,action);

  4. (Optional) the root reducer may combine the output of multiple reducers into a single state tree.

    • combineReducers({reducerA, reducerB, ...reducerN})

  5. Redux saves the complete state tree at the store

    • the updated state is available by store.getState();

Installing Redux

  • Installing React Redux

    • npm install --save react-redux

  • Presentational & Container Components

    • Presentational Components

    • Container Components

      • Connect(mapStateToProps, mapDispatchToProps) function

      • mapStateToProps

      • mapDispatchToProps

  • Passing the Store

 

import Component from “url/to/presentational/component”;
import * as actions from "url/to/actions";

const mapStateToProps = state => ({
    twigs: state.twigs,
    activeTwigs: getTwigsByStatus(state.twigs, "Active")
});

const mapDispatchToProps = dispatch => ({
    load: (id) => dispatch(actions.load()),
    save: (data) => dispatch(actions.save(data)),
    error:()=> dispatch(actions.error())
});

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

Redux's Lifecycle

Provider

The Redux’s provider is a savvy function that allows us to magically (vía context) make the store accessible to every connected app’s container. This saves us the tedious task of passing the store down as a prop to any component within our app.

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import todoApp from './reducers'
import App from './components/App'

let store = createStore(todoApp)
render(
 <Provider store={store}>
   <App />
 </Provider>,
 document.getElementById('root')
)

Async Actions

Every Redux action needs to be a plain object, a pure function, so then we create a function that generates the desired action payload.

Redux Thunk is a middleware that checks when an action creator returns a function, so it can execute it internally. This function isn’t required to be pure, allowing side effects, like executing asynchronous API calls and also dispatching other actions.

 

import thunkMiddleware from 'redux-thunk'

const store = createStore(
  rootReducer,
  applyMiddleware(
    thunkMiddleware,
    loggerMiddleware
  )
)
function loadTask(id) {
  return dispatch => {
    dispatch(clearTasks());
    return axios
    .get(`https://www.example-api.com/${id}`)
    .then(json => dispatch(receiveTasks(id))
    .catch(error=> dispatch(loadError(error))));
  }
}

Middleware

A Redux middleware is a code you put between dispatching an action, and the moment it reaches the reducer. People use Redux middleware for logging, crash reporting, talking to an asynchronous API, routing, and more.

const logger = store => next => action => {
  console.log('dispatching', action);
  let result = next(action);
  console.log('next state', store.getState());
return result;
}
function logger(store) {
  return function (next) {
    return function (action) {
      console.log('dispatching', action);
      var result = next(action);
      console.log('next state', store.getState());
      return result;
    };
  };
};

Redux Logger

const thunk = store => next => action => {
  if (typeof action.then !== 'function') {
    return next(action);
  }

return Promise
    .resolve(action)
    .then(store.dispatch);
}
function thunk(store) {
  return function (next) {
    return function (action) {
      if (typeof action.then !== 'function') {
        return next(action);
      }

      return Promise
        .resolve(action)
        .then(store.dispatch);
    };
  };
};

Redux-thunk

Testing

Jest

Jest is a test runner created by Facebook, featuring a fast setup environment with instant feedback with built-in coverage and Snapshot testing.

 

Jest parallelizes test runs across workers to maximize performance.

Is built on top of Jasmine, so you can expect the same assertion features.

Enzyme

Enzyme is an Airbnb javascript testing utility for React that makes easy to assert, manipulate, and traverse your React's component output

Enzyme API

  • Shallow rendering
  • Full Rendering
  • Selectors

Shallow Rendering

Shallow rendering is used to constrain yourself to test a component as a unit, and to ensure that your tests aren't indirectly asserting on the behavior of child components.

class Foo extends React.Component{
    render(){
        return(<div><Bar/></div>);
    };
}

import {shallow} from "enzyme";
describe('<MyComponent />', () => {
  it('should render the <Foo /> component', () => {
    const wrapper = shallow(<MyComponent />);
    expect(wrapper.find(Foo).length).toBe(1);
    expect(wrapper.find("div").length).toBe(1);
  });
});

Full Rendering

Full DOM rendering is used for cases where you need to fully interact with your DOM APIs, using lifecycles like componentDidMount, etc.

You must also configure a browser environment, like jsdom, which is a headless browser implemented in js.

class Foo extends React.Component{
    render(){
        return(<div><Bar/></div>);
    };
}

class Bar extends React.Component{
    render(){
        return(<div><p>Bar</p></div>);
    };
}

import {shallow} from "enzyme";
describe('<MyComponent />', () => {
  it('calls componentDidMount', () => {
    sinon.spy(Foo.prototype, 'componentDidMount');
    const wrapper = mount(<Foo />);
    expect(Foo.prototype.componentDidMount.calledOnce).to.equal(true);
    expect(wrapper.find("p").text()).toContain("Bar");
  });
});

Enzyme Selectors

Enzyme offers support to traverse a React component via selectors. The can be a:

  1. Valid css selector.
  2. React Component or display name.
  3. Object Property selector.
//CSS selectors
wrapper.find('.foo');
wrapper.find('div');
wrapper.find('#foo-bar');

//React Component
import Foo from "./Foo";
wrapper.find(Foo);

//Component¡s display name
wrapper.find("Foo");

//Obejct Property Selector
wrapper.find({ foo: 3 });
wrapper.find({ bar: false });

Thank you

GFT Group

Anyul Rivas

Technical Lead

 

Av. Alcalde Barnils, 69-71.

08174 Sant Cugat, Spain.

 

T +34 711 620 420

anyul.rivas@gft.com

React for Redux

By Anyul Led Rivas

React for Redux

React for Redux workshop

  • 318