Introduction

to

Redux

(Without React)

Objectives

  • Define what redux is

  • Describe actions and reducers in redux

  • Describe methods on the redux store

Redux

A popular state management library 

Models the application state as a single JavaScript Object

Created by Dan Abramov and Andrew Clark

Inspired by Facebook Flux and Elm

Often used with React but it does not depend on React

Redux Installation

npm install --save redux

Action

A plain JavaScript object that must have a key called type and a string value

{
  type: "LOGOUT_USER"
}

The action can have any number of additional keys

Reducer

A function that accepts the state and an action and returns a new state (entire state object)

function rootReducer(state={}, action) {
  switch(action.type) {
    case "LOGOUT_USER":
      return {...state, login: false}
    case "LOGIN_USER":
      return {...state, login: true}
    default:
      return state;
  }
}

Creating A Store

Use the Redux createStore function which accepts the root reducer as a paramter

const store = Redux.createStore(rootReducer);

Changing the State

The only way to change the state is by calling dispatch

const store = Redux.createStore(rootReducer);
store.dispatch({
  type: "LOGIN_USER"
});

Getting The State

You can get the state of the Redux store using getState

const store = Redux.createStore(rootReducer);
store.dispatch({
  type: "LOGIN_USER"
});

const newState = store.getState();

Listening For Changes

You can add a listener to see when the state has changed

const store = Redux.createStore(rootReducer);
const changeCallback = () => {
  console.log("State has changed",
              store.getState());
}
const unsubscribe = 
   store.listen(changeCallback);

Redux State Change

dispatch(action)

reducer(currentState, action)

newState

Invoke Listeners (UI Changes)

React

With

Redux

Objectives

  • Describe react-redux

  • Use the provider component to share a store

  • Use connect to mapStateToProps and mapDispatchToProps

React-Redux

A library to facilitate integrating React with Redux

Exposes a Provider component and a connect function

Handles: Listeners, passing in state to a component

React-Redux Install

npm install --save react-redux

Provider

import React from 'react';
import ReactDOM from 'react-dom';
import {Provider} from 'react-redux';
import {createStore} from 'redux';
import rootReducer from './reducers';

const store = createStore(rootReducer);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

Connect: Wrapping A Component

import React from 'react';
import {connect} from 'react-redux';
const BoldName = ({name}) => (
  <strong>{name}</strong>
);

const mapStateToProps = state => (
  { name: state.name }
);

export default 
  connect(mapStateToProps, null)(BoldName);

Connect: Wrapping A Component

import React from 'react';
import {connect} from 'react-redux';

const DelName = ({delName}) => (
  <button type="button"
    onClick={delName}>DELETE</button>
);

const mapDispatchToProps = (
  dispatch, ownProps
) => (
  { 
    delName: () => (dispatch({
      type: "DEL_NAME" 
    }))
   }
);

export default 
  connect(null, mapDispatchToProps)(DelName);

Using Wrapped Components

import React from 'react';
import BoldName from './BoldName';
import DelName from './DelName';

const App = () => (
  <div>
    <BoldName />
    <DelName />
  </div>
);

Your

Turn

Redux

Refactor Exercise

Redux

Refactor Solution

Organizing Redux

Objectives

  • Define a presentational component vs a container component

  • Define combine reducers

  • Define action creators

  • Describe a folder structure for redux

Presentational Component

A component that is primarily concerned with how things look

It is often a stateless functional component, but does have to be

Container Component

Usually a stateful component that deals with application data

Often created using higher order components like connect or withRouter

Dan Abramov's

Presentational And Container Components

combineReducers

A redux function that allows for reducer composition

Each reducer is only responsible for its piece of the entire state object

import {combineReducers} from 'redux';
import currentUser from './currentUser';
import messages from './messages';

const rootReducer = combineReducers({
  currentUser,
  messages,
});

export default rootReducer;

combineReducers

const messages = (state=[], action) => {
  switch(action.type) {
    case "LOAD_MESSAGES":
      return [...action.messages];
    case "ADD_MESSAGE":
      return [action.message, ...state];
    default:
      return state;
  }
};

export default messages;

Messages Reducer

Action Creators

A function that returns an action object

const mapDispatchToProps = dispatch => ({
  onLogout() {
    dispatch({
      type: "USER_LOGOUT"
    })
  },
});
const mapDispatchToProps = dispatch => ({
  onLogout() {
    dispatch(actions.userLogout())
  },
});

Redux Directory Structure

actions

components

containers

reducers

src

index.js

Todos

Redux

Example

Redux

Thunk

(Middleware)

Objectives

  • Define redux middleware

  • Define redux thunk and show a use case

Redux Middleware

Same idea as middleware in NodeJS

Code that is executed after an action is dispatched but before a reducer handles the action

Redux Thunk

Middleware for handling async actions

(API requests)

npm install --save redux-thunk

Middleware Setup

With Redux Thunk

import { createStore,
        applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { createLogger } from 'redux-logger'
import reducer from './reducers'

const middleware = [ thunk ]
if (process.env.NODE_ENV !== 'production') {
  middleware.push(createLogger())
}
const store = createStore(
  reducer,
  applyMiddleware(...middleware)
)

Thunk Action Creator

export const receivePosts = (reddit, json) => ({
  type: RECEIVE_POSTS,
  reddit,
  posts: json.data.children.map(child => child.data),
  receivedAt: Date.now()
}

const fetchPosts = reddit => (
  dispatch => {
    dispatch(requestPosts(reddit))
    return fetch(`https://www.reddit.com/r/${reddit}.json`)
      .then(response => response.json())
      .then(json => dispatch(receivePosts(reddit, json)))
  }
)

Redux Examples Async

Warbler Final Project

Redux + React Router

Update Blocking

HTML5

History

Object

Objectives

  • Modify the address bar with history.pushState

  • Describe how bookmarking a single page application works

History

HTML5 introduced the history object

history.back();

history.forward();

history.pushState({}, 'title', '/newpage');

Allows javascript to manipulate the browser history

Changes To History

Changes the url address bar

 

Changes the browser's local navigation history

 

DOES NOT cause the browser to make a GET request

History

More Info

What Will We Use History For?

To create a good single page application, we want to have it feel like a normal website

Browser back button, internal links, etc should all seem to behave like a page with server rendering

Server Side Rendering

GET /

index.html (fully rendered)

GET /signin

/signin (fully rendered)

Node.js

HTML

Client Side Rendering (React)

GET /

index.html (only div id="root")

Node.js

React

Render react app

Change address to /signin

Render /signin component

Handling A Bookmark

GET /user/55

index.html (only div id="root")

Node.js

React

Render react app with

correct user component

Bookmarked /user/55

Server side support is required

React Router

Objectives

  • Describe React Router v4

  • Differentiate BrowserRouter vs HashRouter

  • Use Link, Switch, and Route components

React Router v4

A library to manage routing in your single page application

Version 4 was launch in March 2017 and has some big changes compared to v3

Version 4 is a declarative api that uses components to make rendering decisions

BrowserRouter vs HashRouter

BrowserRouter uses the history object and makes changes to the URL, the hash router adds hashes to the url instead

BrowserRouter

HashRouter

/

/users

/users/57492/messages

/#

/#users

/#users/57492/messages

BrowserRouter vs HashRouter

BrowserRouter requires server support

HashRouter does not require server support

Always choose BrowserRouter if you are able

React Router Install

npm install --save react-router-dom

React Router Setup

import React from 'react';
import ReactDOM from 'react-dom';
import {
  BrowserRouter as Router
} from 'react-router-dom';
import App from './App';

ReactDOM.render(
  <Router>
    <App />
  </Router>,
  document.getElementById('root')
);

Switch And Route

import React, {Component} from 'react';
import {
 Switch, Route
} from 'react-router-dom';

const Homepage = () => (<div>HOMEPAGE</div>);
const About = () => (<div>ABOUT</div>);

const SwitchDemo = () => (
  <Switch>
    <Route path="/about" component={About}/>
    <Route path="/" component={Homepage}/>
  </Switch>
);

Link

import React from 'react';
import {Link} from 'react-router-dom';
import SwitchDemo from './SwitchDemo';

const App = () => (
  <div>
    <Link to="/">HOME</Link>
    <Link to="/about">ABOUT</Link>
    <div style={{fontSize: '3em',
                 margin: '25px'}}>
      <SwitchDemo/>
    </div>
  </div>
);

NavLink

import React from 'react';
import {NavLink} from 'react-router-dom';
import SwitchDemo from './SwitchDemo';

const s={color: "red"}; //active style
const App = () => (
  <div>
    <NavLink exact activeStyle={s} to="/">
     HOME
    </NavLink>
    <NavLink exact activeStyle={s} to="/about">
      ABOUT
    </NavLink>
    <div style={{fontSize: '3em',margin: '25px'}}>
      <SwitchDemo/>
    </div>
  </div>
);

React Router

(Continued)

Objectives

  • Use URL parameters for a Route

  • Define Route props

  • Define withRouter

  • Passing your own props to a component in Route (render vs component)

URL Parameters

import React from 'react';
import {
 Switch, Route
} from 'react-router-dom';

const Homepage = () => (<div>HOMEPAGE</div>);
const Name = ({match}) => (
  <div>Hello, {match.params.name}</div>
);
const SwitchDemo = () => (
  <Switch>
    <Route path="/:name" component={Name}/>
    <Route path="/" component={Homepage}/>
  </Switch>
);

Route Props

A component inside of a Route gets 3 props

match - info about how the url matches the route component

location - where you are now, similar to window.location

history - similar to html5 history object, allows explicit changes to the url

withRouter

If a component is not rendered inside of a Route component, you can use withRouter to get route props

withRouter Example

import {
 withRouter, Switch, Route
} from 'react-router-dom';

const SwitchDemo = ({history}) => (
  <div>
    <Switch>
      <Route path="/:name" component={Name}/>
      <Route path="/" component={Homepage}/>
    </Switch>
    <button onClick={() => history.push('/')}>
      Go Home
    </button>
  </div>
);
export default withRouter(SwitchDemo);

Route:

Render vs Component

The route component can either use render or component (never both)

Use render to pass custom props to your component

Route Render Example

import {Route} from 'react-router-dom';
const teachers = ['Tim', 'Colt', 'Matt', 'Elie'];
const Teachers = ({teachers}) => (
  <ul>
    {teachers.map((teach, ind) => (
      <li key={i}>{teach}</li>
     ))}
  </ul>
);

const App = () => (
  <Route path="/teachers" render={props => (
    <Teachers {...props} teachers={teachers} />
  )}/>
);

React Router Docs

React Router Exercise