React and Redux Crash Course with Steven Nunez
Published Tuesday, December 13, 2016
A crash course in React and Redux from Steven Nunez, Lead Instructor/Developer at Flatiron School (part 1).
Goals:
- Learn through doing
- Knowledge up on cool new tech
Why Redux?
- “Surprisingly simple” says Steven
- Very specific purpose
- Supes popular
- Lots of high quality curriculum available from Flatiron School
- They hired Dan Abramov
What is Redux?
- State Management
The end.
- Has nothing to do with the web.
- Has nothing to do with React.
- Nice and simple.
Is it Flux?
Not really. Flux has a very specific set of parts: dispatcher, actions sent by dispatcher, etc. Redux has a lot of the same players, but follows slightly different pattern.
Why Redux was hard for Steven
Seems like a lot of stuff at first.
The part that’s Redux is actually fairly small. Redux covers Reducers, Dispatch, middleware. The rest is mostly Redux+React. And then conventions like ALL_THE_CAPS and Action Creators are separate conventions (oof).
Exercise: Simple Chat App
Building a chat app
code lives at: https://github.com/StevenNunez/redux_chat_hedgehog
brew install elixir
mix phoenix.new redux_chat_hedgehog
(using phoenix bc it has nice tools for modern web workflow)- comment out db username and password in
config/dev.exs
mix deps.get
mix ecto.create
- rm boilerplate markup in
web/templates/page/index.html
mix phoenix.server
npm install --save react redux
npm install --save babel-preset-react
npm install --save react-dom
npm install --save axios
(for fetch requests)npm install --save redux-thunk
- wipe out boilerplace in
web/static/js/app.js
- update
brunch-config.js
- plugins config should look like this:
// Configure your plugins plugins: { babel: { // Do not use ES6 compiler in vendor code ignore: [/web\/static\/vendor/], presets: ['react', 'es2015'] } }, //...
- in
web/static/js/app.js
:import { createStore } from 'redux'
- with ES6 module system, couple ways to export from a class
- can export as a constant
- can export default
import Something from 'aFile'
gets you the defaultimport { thing1, thing2 } from 'aFile'
gets specific stuff (constants, functions, etc)- that way when you open a file, you can see up top what all the collaborators are
- About Redux store:
- In Redux, store is really stupid (aka simple)
- Store can receive messages and do a thing
- Store has method called dispatch:
store.dispatch({type: 'whatevs'})
- dispatch requires
type
key - we can add kinda like an event listener where when state changes, can re-run code:
store.subscribe(()=>{ // whenever receives message, function will be called console.log(store.getState()); })
- note: you have to pass a function to createStore - chain of events: dispatch then call subscribe - function that you pass to store is known as the Reducer
function counter() { // this is our Reducer return 1; } const store = createStore(counter);
- About the Reducer
- first argument we receive in the reducer is state
- common/best practice to define default state
- second argument is the action
- super simple example:
function counter(state=0, action) { // this is our Reducer switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } }
- Another thing: Combined Reducers
- composing reducers
- multiple reducers to handle different things, manage different parts of the page
function counter(state=0, action) { // this is our Reducer switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } function lastMessage(state='No Message Yet', action) { // Another reducer switch (action.type) { case 'INCREMENT': return 'Was incremented!'; case 'DECREMENT': return 'Was decremented!'; default: return state; } } const rootReducer = combineReducers({ // map reducers counter: counter, lastMessage: lastMessage }); const store = createStore(rootReducer);
- each reducer will now receive dispatched message and then execute accordingly
// state of web/static/js/app.js so far import { createStore, combineReducers } from 'redux' import React from 'react' import { render } from 'react-dom' function counter(state=0, action) { // this is our Reducer switch (action.type) { case 'INCREMENT': return state + 1; case 'DECREMENT': return state - 1; default: return state; } } function lastMessage(state='No Message Yet', action) { // Another reducer switch (action.type) { case 'INCREMENT': return 'Was incremented!'; case 'DECREMENT': return 'Was decremented!'; default: return state; } } const rootReducer = combineReducers({ // map reducers counter: counter, lastMessage: lastMessage }); const store = createStore(rootReducer); const App = (props) => { return ( <div> <div>The current value is: {props.counterValue}</div> <div>The last message was {props.lastMessage}</div> <div> <button onClick={()=> store.dispatch({type: 'INCREMENT'})}> Up </button> <button onClick={()=> store.dispatch({type: 'DECREMENT'})}> Down </button> </div> </div> ) } store.subscribe(function () { render( <App counterValue={store.getState().counter} lastMessage={store.getState().lastMessage} />, document.querySelector('#root') ); }); store.dispatch({type: 'Banana'});
- Concepts: immutability and functional purity
- immutability helps w/ performance
- pure function == function that if you give it same inputs, will always return same thing, not affected by outside world
- reducers are always pure
- no promise resolution inside reducers
- How we handle async stuff in Redux:
- can’t emit a Promise, per se
- but can take advantage of middleware
- after action is dispatched, moment where you can transform message into format you want, make it a plain ol’ object
The End
More next week!