React Redux: Difference between revisions

From bibbleWiki
Jump to navigation Jump to search
Line 128: Line 128:
* Enforce by using products like Immer, Immutable.js etc
* Enforce by using products like Immer, Immutable.js etc


==== Create root Reducer ====
== Create root Reducer ==
This is used to combine reducers. In this example there is only one but there will be more.
This is used to combine reducers. In this example there is only one but there will be more.
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Line 141: Line 141:
</syntaxhighlight>
</syntaxhighlight>


==== Configure Store ====
== Configure Store ==


===== Simple Store =====
=== Simple Store ===
The simplest way to do this is   
The simplest way to do this is   
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Line 154: Line 154:
</syntaxhighlight>
</syntaxhighlight>


===== Store with redux devtools =====
=== Store with redux devtools ===
However here is a useful alternative which allows the use of redux devtools
However here is a useful alternative which allows the use of redux devtools


Line 173: Line 173:
</syntaxhighlight>
</syntaxhighlight>


===== Store with logging =====
=== Store with logging ===
This will show action prev state and next state
This will show action prev state and next state
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Line 184: Line 184:
);
);
</syntaxhighlight>
</syntaxhighlight>
===== Combining Middleware =====
=== Combining Middleware ===
Note the middleware can be combined by comma separated values.
Note the middleware can be combined by comma separated values.
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Line 193: Line 193:
</syntaxhighlight>
</syntaxhighlight>


==== Connect Application to redux store ====  
== Connect Application to redux store ==


Create a redux store and pass it via a provider by surrounding the App with the redux provider
Create a redux store and pass it via a provider by surrounding the App with the redux provider
Line 215: Line 215:
</syntaxhighlight>
</syntaxhighlight>


==== Connect Component to redux ====  
== Connect Component to redux ==
To connect a component to redux you need to call the connect function which takes two functions mapStateToProps and optionally mapDispatchToProps and pass your page as an argument
To connect a component to redux you need to call the connect function which takes two functions mapStateToProps and optionally mapDispatchToProps and pass your page as an argument


Line 222: Line 222:
</syntaxhighlight>
</syntaxhighlight>


===== mapStateToProps =====
=== mapStateToProps ===
This is when you pass a mapping of your state to your properties and your own props which might not be in state.
This is when you pass a mapping of your state to your properties and your own props which might not be in state.
<syntaxhighlight lang="javascript">
<syntaxhighlight lang="javascript">
Line 232: Line 232:
</syntaxhighlight>
</syntaxhighlight>


===== mapDispatchToProps =====
=== mapDispatchToProps ===


Manually wrap each create with dispatch
Manually wrap each create with dispatch

Revision as of 07:28, 3 June 2020

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>        )
    }
}

Redux Flow

Initial Steps for Redux

You need 9 steps to create a redux app

* Create Action
* Create Reducer
* Create Root reducer
* Configure Store
* Instantiate Store
* Connect Application to redux store
* Connect Component to redux
* Pass props via connect
* Dispatch action

Create Action

Pretty simple create function which returns an action. Don't forget to export

export function createCourse(course) {
   return {type: "CREATE_COURSE", course} 
}

Create Reducer

Simple Example

Note the reducer takes a state and an action. In this case state is an array. Also note the return is a new copy of state with the passed in course appended.

export default function courseReducer(state = [], action) {
  switch (action.type) {
    case "CREATE_COURSE":
      return [...state, { ...action.course }];
    default:
      return state;
  }
}

Rules

Reducer must not

  • Mutate arguments
  • Perform side effects
  • Call non-pure functions

Pure Functions

Reducers must use pure functions where the outcome is always predictable. i.e. you cannot call mutable function. e.g.

 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

Create root Reducer

This is used to combine reducers. In this example there is only one but there will be more.

import { combineReducers } from "redux";
import courses from "./courseReducer";

const rootReducer = combineReducers({
  courses,
});

export default rootReducer;

Configure Store

Simple Store

The simplest way to do this is

import { createStore } from "redux";
import rootDeducer from "./reducers";

export default function configureStore(initialState) {
  return createStore(rootDeducer, initialState);
}

Store with redux devtools

However here is a useful alternative which allows the use of redux devtools

import { createStore, applyMiddleware, compose } from "redux";
import rootDeducer from "./reducers";
import reduxImmutableStateInvariant from "redux-immutable-state-invariant";
export default function configureStore(initialState) {
  const composeEnhancers =
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

  return createStore(
    rootDeducer,
    initialState,
    composeEnhancers(applyMiddleware(reduxImmutableStateInvariant()))
  );
}

Store with logging

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)
);

Combining Middleware

Note the middleware can be combined by comma separated values.

createStore(
    rootDeducer,
    initialState,
    composeEnhancers(applyMiddleware(reduxImmutableStateInvariant(), logger))

Connect Application to redux store

Create a redux store and pass it via a provider by surrounding the App with the redux provider

import React from "react";
import { render } from "react-dom";

import {Provider} from "react-redux"
import configureStore from "./redux/createStore"

const store = configureStore();

render(
    <Provider store={store}>
        <App />
    </Provider>
  document.getElementById("app")
);

Connect Component to redux

To connect a component to redux you need to call the connect function which takes two functions mapStateToProps and optionally mapDispatchToProps and pass your page as an argument

export default connect(mapStateToProps, mapDispatchToProps)(CoursesPage)

mapStateToProps

This is when you pass a mapping of your state to your properties and your own props which might not be in state.

function mapStateToProps(state, ownProps) {
    return {
     courses: state.courses
    }
}

mapDispatchToProps

Manually wrap each create with dispatch

function mapDispatchToProps(dispatch) {
    return {
        loadCourses() => {
           dispatch(loadCourses())
        },
        createCourses() => {
            dispatch(createCourses())
        },
        upateCourses() => {
            dispatch(upateCourses())
        } 
    }
} 

// In the component
this.props.loadCourses()

Manually write the dispatch

function mapDispatchToProps(dispatch ) {
   return {
       createCourse: course => dispatch(courseActions.createCourse(course))
   }
}

// Usage
this.props.createCourse(this.state.course)

Using bindActionsCreator from redux

import {bindActionCreators} from "redux"
...
function mapDispatchToProps(dispatch ) {
   return {
       actions: bindActionCreators(courseActions, dispatch)
   }
}

// usage
this.props.actions.ceateCourse(this.state.course)


Using object

function mapDispatchToProps(dispatch) {
     loadCourses
} 

// usage
this.props.loadCourses()

Add Feature

* Create Action
* Enhance Reducer
* Connect component
* Dispatch action

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]]