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
Redux
By Elie Schoppik
Redux
- 2,110