React/Redux walkthrough

by John Mitsch

irc: jomitsch

jomitsch@redhat.com

Overview

  • ES6+ syntax you may see in React
  • React refresher
  • Redux tutorial
  • Walkthrough of stupidly simple application w/ React + Redux
  • Follow along walkthrough of adding a new React page to Katello
  • Are we recording?

ES6+

ES6+

Let and Const

// old way
var fruit = "banana";

// new way in es6+
const fruit = "banana";
// fruit variable is fixed and can't change
fruit = "strawberry"; // this will error

// let can be used to reassign variables
let vegetable = "zucchini";
// let can be reassigned
vegatable = "carrot"; // no errors!

// but wait! One exception here
// Arrays can be modified even when declared with const
const favoriteSushiRolls = ["california", "spicy tuna"];
favoriteSushiRolls.push("dragon");

ES6+

Arrow Functions

// Old way
function sayHi() {
  console.log("hello from es5!");
}

// New way
const sayHi = () => {
  console.log("hello from es6!");
}

// Can take arguments, don't need parenthesis with one arg
const sayHi = location => {
  console.log(`hello from ${location}`);
}

const sayHi = (location, greeting) => {
  console.log(`${greeting} from ${location}`);
}

// Can even do one liner
const sayHi = location => console.log(`hello from ${location}`);

ES6+

Spread operators

// Can use spread to "unpack" array
const arr = [1,2,3,4];
[...arr]
// [ 1, 2, 3, 4 ]

// can use it to quickly add to array
[...arr, 5, 6]
// [ 1, 2, 3, 4, 5, 6 ]

// can use array as arguments
Math.max(...arr)
// 4

ES6+

object deconstruction

const appleMap = { fiji: "yum", pinkLady: "gross" };

const { fiji, pinkLady } = appleMap;

console.log(fiji);
// yum

console.log(pinkLady);
// gross

React

React basics

  • JSX
    • Looks a lot like HTML
    • JSX gets pre-compiled into javascript
  • Components
    •  Modular pieces of your application that handle logic and render JSX
  • Two types of data components handle:
    •  Props: fixed data that is passed to the component, think of it like passing arguments to a function
    • State: dynamic data that is managed by the component 

   

React Component

 

// Importing React + Component
import React, { Component } from 'react';

// extend React Component class to create new component
class MyAwesomeComponent extends Component {
  // class constructor
  constructor(props) {
    // pass props to parent class
    super(props);

    // create initial state
    this.state = {
      value: ""
    }
  }

  
  componentDidMount() {
    // Do stuff when component mounts here
  }

  // Render function returns JSX
  render() {
    const { someCoolThing } = this.props;
    return (
      <div>{ someCoolThing }</div>
    )
  }
}

React Component

 

// Use component and pass in props
<MyAwesomeComponent someCoolThing={"snuggies"} />

Example App

Redux

Why Redux?

Component

Component

Component

Component

Component

Component

Component

Component

Just React

React with Redux

Store

State transfer between components

Redux

  • Redux manages a global state in a store
    • One large JavaScript object
  • State is never changed directly, the object is immutable.
  • State is changed through a reducer, which is triggered by an action

Redux Store

  • Global object managing state
  • Store actions
    • getState() - returns the current state of your application
    • dispatch(action) - The only way to trigger a state change, dispatches an action.
    • subscribe(listener) - register a callback (function) that redux will call anytime the store is updated.
const { createStore } = Redux; 
const store = createStore(myReducer);

Redux Actions

  • Actions are payloads of information.
  • They a way of sending information to the store, by way of a reducer
  • The only requirement is a 'type' attribute
store.dispatch({
  type: ADD_FRUIT,
  fruit: "pear",
})

Redux Reducers

  • Reducers take the previous state of the application and return the next state of the application
    • Reducer takes state and an action and returns new state
    • We are not modifying the actual state!!
      • Always return a new state
  • If reducer gets undefined state, return "base" or "default" state

Redux Reducers

const fruits = (state = [], action) => {
  switch (action.type) {
    case "ADD_FRUIT":
      return [
        ...state,
        action.fruit
      ];
    default:
      return state;
  } 
};

Reducers will typically be one large switch statement

Redux Reducers

// remove item from fruits array at index
[ ...fruits.slice(0,index), ...fruits.slice(index+1)]

// modify item at index
[ ...fruits.slice(0,index), newFruit, ...fruits.slice(index+1)]

// Can create new object based on existing object with Object.assign (es6)
fruitProperties = { orange: "keeps you healthy" };
newFruitProperties = Object.assign({}, 
                                   fruitProperties, 
                                   {pomegranate: "stains your clothes"});
console.log(newFruitProperties)
//{ orange: 'keeps you healthy',
//  pomegranate: 'stains your clothes' }

// Can also use spread operator for objects (es7)
{ ...fruitProperties, pomegranate: "stains your clothes"}

There are ways to return new state object using es6+ syntax

Combining Reducers

// import Redux's combine reducers method
const { combineReducers, createStore } = Redux

// import custom reducers from app
import todos from './todosReducer.js';
import favoriteFruits from './favoriteFruitsReducer.js'

// combine reducers
const myStrangeApp = combineReducers({
  todos: todos,
  favoriteFruits: favoriteFruits,
});

// or use es6 shorthand to do the same
const myStrangeApp = combineReducers({
  todos,
  favoriteFruits,
});

createStore(myStrangeApp);

Reducers manage one aspect of information, we have to combine them and create the store.

One issue

How do we access this information in our components?

Redux

We can keep passing the store to our components

<SomeComponent store={store} />

But that doesn't seem very efficient for a large application with many parent-child component relationships...

Redux

Instead we can use the Provider class from react-redux package

import { Provider } from 'react-redux';

<Provider store={store}>
   <MyApp />
</Provider>

Redux

We can connect to this provider and use the global state from the store as props.

import FruitComponent from './FruitComponent';
import { connect } from 'react-redux';

// mapping state to props, we want to use the 'fruits' list from the redux store
// as a prop in the component
const mapStateToProps = state => (state.fruits);

// We want to use an action as a prop in our component, so we pass
// map 'addFruitToList' function as a prop here.
const mapDispatchToProps = (dispatch) => {
  return {
    addFruitToList: (fruit) => {
      dispatch({
        type: "ADD_FRUIT",
        fruit: fruit,
      })
    }
  }
}

// connect adds these as props to our component and the component is exported
export default connect(
  mapStateToProps,
  mapDispatchToProps,
)(FruitComponent);                 

Questions?

Redux in example app

Katello walkthrough

I'll be creating a packages UI for katello, I encourage you to follow along with me in your own environment. If you would like to use another API endpoint, that is fine too.

React and Redux

By jomitsch

React and Redux

  • 36
Loading comments...