React Redux
Unidirectional
Data only flows one direction
Pure Functions
- Like static is c#/c++ only using inputs to produce outputs
function multiply(a, b)
{
return a * b;
}
- No side effects
- Also yields same result
Immutability
== Way to be immutable ES6 allows us to copy objects using Object.assign e.g.
var state = {color:'red', name: 'Adam', point:5}
var state2 = Object.assign({}, state, {point:50})
You can use map or filter, reduce, concat or spread as these all create a new object
Tools to enforce immutability
- Using trust with your team - relies on good code reviews
- Use redux-immutable-state-invarient a package to detect problems (not for prod)
- Enforce by using products like Immer, Immutable.js etc
Example
Note ... is new syntax for spread
Stores often are stored in stores/configurationStore.js at the same level as compontents
import {createStore} from 'redux'
var defaultState = {
originalAmount: 0.00
};
function amount(state = defaultState, action) {
if(action == = 'CHANGE_ORIGIN_AMOUNT') {
return {
...state,
originalAmount: action.data
}
// return Object.assign({},state, {originalAmout: action.data})
}
}
var store = createStore();
store.subscribe(function() {
console.log('state', store.getState());
})
store.dispatch({type:'CHANGE_ORIGIN_AMOUNT', data: 30.00});
store.dispatch({type:'', data: 30.00});
store.dispatch({type:'', data: 30.00});
Example Component
Show us subscribing to the store to make sure updates passed down to the components.
class MainConponent extends React.Component {
componentDidMount() {
store.subscribe( () => {
this.setState({});
} )
}
render() {
return (
<div>
<Conversion originalAmount={store.getState().originalAmount} />
</div> )
}
}
Reducer
Rules
Reducer must not
- Mutate arguments
- Perform side effects
- Call non-pure functions
Pure function are those where the answer is predictable and the same every time e.g.
function add(a, b) {
return a + b;
}
Example of reducer
Here is a reducer which is wrong because it changes the original state
function myReducer(stat, action) {
switch(action.type) {
case "INCREMENT_COUNT":
state.counter++
return state
default:
return state
}
The above is wrong because the state is change. Here is the corrected version
function myReducer(stat, action) {
switch(action.type) {
case "INCREMENT_COUNT":
return { ...state, counter: state.count + 1}
default:
return state
}
Example Component with provider
This removes the need to subscribe and pass the original amount
class MainConponent extends React.Component {
render() {
return (
<div>
<Conversion/>
</div> )
}
}
ReactDOM.render(<Provide store={store}><MainComponent /></Provider>,getElementById('container'));
We need to map the state back to the properties on initialisation of the component. We do this via connect
export default connect(function mapStateToProps(state,props)) {
return {
originAmount : state.originalAmount
}
}
Adding logging to redux
This will show action prev state and next state
import {applyMiddleware,createStore } from 'redux';
import logger from 'redux-logger';
var store = createStore(
amount,
applyMidddleware(logger)
);
Setting types using keyMirror
Just a nice little tip. keyMirror stops you having to type more for const e.g.
export var ActionType = {
const THIS_IS_A_CONST = "THIS_IS_A_CONST";
};
becomes
export var ActionType = keyMirror ({
THIS_IS_A_CONST = null
});
Example on Git hub
You can find an example at [[1]]
how it works
In the code handleOriginalAmount is fired by a change
this.props.dispatch(actions.changeOriginAmount(newAmount));
In the store there is a reducer which has the actions
function amount(state = defaultState, action) {
switch (action.type) {
case (types.CHANGE_ORIGIN_AMOUNT):
return {
...state,
originAmount: action.data.newAmount
}
case (types.CHANGE_DESTINATION_AMOUNT):
return {
...state,
destinationAmount: action.data.newAmount
}
case (types.CHANGE_ORIGIN_CURRENCY):
return {
...state,
originCurrency: action.data.newCurrency
}
Here is an example of an action
export function changeOriginAmount(newAmount) {
return {
type:types.CHANGE_ORIGIN_AMOUNT,
data:{newAmount: newAmount}
}
}
Because the page is connected to redux via the connect when state changes the properties change
export default connect((state, props) => {
return {
originAmount: state.amount.originAmount,
destinationAmount: state.amount.destinationAmount,
originCurrency: state.amount.originCurrency,
destinationCurrency: state.amount.destinationCurrency,
conversionRate: state.amount.conversionRate,
feeAmount: state.amount.feeAmount,
totalCost: state.amount.totalCost,
errorMsg: state.error.errorMsg
}
})(Conversion);