FSA 1702, March 30, 2017
Predictable State Container for Javascript Apps
We dispatch actions (an expression of intent). Redux changes our state for us in a store, and returns a new state. We don't change our state directly. Then we can REWIND TIME.
State: condition of the app (data) at a particular moment in time
Props: data passed down from a container to a component
State changes, props are just data that is passed around. Although props do change, they do not change as far as the component is concerned. It just receives the props from above.
Some dynamic information is generated by and only needed by that component.
Other information is needed by the application as a whole.
For a countdown: the remaining time is a local state (only the countdown needs to know this). When the countdown reaches zero, only then do we need to let the rest of the app know (global state).
For an input element: local state changes to hold and display value. When we submit we are sending value to global state for processing.
In the context of a game, there is a set of finite possible actions that changes state.
Separate from React. It is a way of organizing state. It does not depend on React.
Solving the problem of: opaque and nondeterministic applications, where many elements are linked together causing re-renders, server requests etc in long, confusing chains.
- a small tool or containing, accessing and affecting a state of information (often called a 'state')
- often part of React architecture (though not a necessary part)
- gateway drug to strictly functional programming
NOT:
- not a React-specific tool
- not react-redux (that's just an npm module)
- not so useful for small applications
- not the only way of managing your state
// for an app.js file
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
const initialState = {x: 0, y: 0}
/* Similar to vanilla JS reducer function
a Redux reducer function does 'action' on state.
Each time an action is performed the action is performed on the state created
by the last action */
const store = createStore( (state=initialState, action) => {
// We always return a new state each time we're in this function
const newState = { x: state.x, y: state.y } // creates a copy of the state, so we never mutate our state
// Switch statements are common in Redux reducer functions
switch (action.type) {
case 'MOVE_UP':
newState.y = state.y + 1;
break;
case 'MOVE_DOWN':
newState.y = state.y - 1;
break;
default:
return state;
}
return newState; // return new changed state or a copy of the original state
} );
// DISPATCH an ACTION to this STORE
store.dispatch({ type: 'MOVE_UP' });
/* State will only change if we pass a valid action to the store.
Dispatch gives the reducer an action */
setTimeout(() => store.dispatch({type: 'MOVE_UP'}, 1000));
setTimeout(() => store.dispatch({type: 'MOVE_UP'}, 2000));
setTimeout(() => store.dispatch({type: 'MOVE_UP'}, 3000));
setTimeout(() => store.dispatch({type: 'MOVE_UP'}, 4000));
setTimeout(() => store.dispatch({type: 'MOVE_UP'}, 5000));
//SUBSCRIBE to a STORE, showing what changes have occurred
store.subscribe( () => {
ReactDOM.render(
<div>
<h1>{ store.getState().x }</h1>
<h1>{ store.getState().y }</h1>
</div>
, document.getElementById('app')
)
console.log(store.getState());
})
console.log(store.getState());
In the past, these two things made our lives difficult. Mixing mutation and async is dangerous... like coke and Mentos. We might get race conditions and other problems.
- Single source of truth. Redux holds a single source for our state. In React we store state all over our app, this is what we change in Redux.
- State is READ-ONLY. Redux changes state if it is allowed -- there is no 'writing' or mutating of the state. You express an intent and Redux makes a new state if its allowed and gives you back a new state.
- Changes to state are made with pure functions (no side-effects). We want our UI layer to be pure functions.
Neither React nor Redux does deep checking for changes – it creates a new state which tells the rest of your app to react to it.
- The singular holder of information
- Receive dispatched signals (actions) meant to affect state
- Provides an interface to access and listen to state
- the part of the store that decides based on signal (action) what the new state is
- creates new states per action rather than mutating previous states
- can be replaced or chunked (broken up into smaller functions)
Download React & Redux Dev Tools, do it!
- Add Redux dev tools as middleware
- React tab in Chrome dev tools
We can rewind time and see the state of our app at different moments. We can also skip certain actions to see what would have happened if we did not do them and 'replayed' the history.
We need a new version of the state rather than mutating the state.
-
we don't want unintended side effects or information out of synch
-
think of it like a git commit
- Merge objects with Object.assign() -- with a new copy of an empty objects
- concat your arrays, rather than pushing, because it returns an array
- [..listOfItems, new item], uses ES6 spread to
Purity:
- referential transparency
- same function with same arguments should always produce the smae output
- no side-effects!
- AJAX calls in our store
- Any randomization or use of dynamic closure information (outside the store)
YOUR REDUCER SHOULD BE A PURE FUNCTION
- combine reducers into a single composite reducer
- Middleware: useful between your dispatch and your reducer. Useful for logging
Redux is a real library that implements Flux (a conceptual idea for managing state in applications).
'Redux' -> Reducers + Flux
------- Action ------
| |
Action --> Dispatcher --> Store --> View