YuWebdesign



Add Redux to React

By YuwebDesign


With npm:

npm install redux react-redux redux-thunk redux-devtools-extension

With yarn:

yarn add redux react-redux redux-thunk redux-devtools-extension


We will create a store directory for redux management

Inside we will create two additional folders: actions and reducers.


We will create two files: myItemReducers.js and reducers.js inside the store/reducers directory.


myItemReducers.js will contain the initial state
and will take care of the state of the particular myItem.


reducers.js will take care of the state of the whole app
(by combining reducers of different components) and then feed it to the store.


Each time an action is dispatched reducer will update the state of the application.


Reducer is a pure function.
It does not mutate the state, when action fires it returns a new copy of the updated state.
To calculate a new state it needs to take in two parameters: previous state and an action.


It is called a reducer because it’s the type of function you would pass to an Array.


It “reduces” a collection of actions and an initial state (of the store)
(performing actions on them) and returns the resulting final state.


myItemReducers.js

const initialState = {
    something: 'something',
}

const myItemReducer = (state = initialState, action) => {
   return state;
};

export default myItemReducer;	

For now myItemReducers will only return initial state, but later we will update this reducer function to change the state depending on the action like so:

import C from './constants';
// import { combineReducers } from 'redux'; 

const initialState = {
    something: 'something',
}

const myItemReducer = (state = initialState, action) => {
     switch (action.type) {

         case C.ACTION_NAME:
             return {
                 ...state,
                 someState: action.payload,
             };
		case C.CLEAR_ERROR : 
      		return state.filter((message, i) => i !== action.payload)

         default:
            return state;
     }
};

export default myItemReducer;	


reducers.js

import { combineReducers } from 'redux';
import myItemReducer from './myItemReducers';

const reducers = combineReducers({
    myItemReducer,
    // someOtherReducer: combineReducers({ reducer#4Name, reducer#5Name })
});

export default reducers;						

To create a store, pass your root reducing function to createStore.


A store is an object
that holds the whole state of the application
in an immutable object tree.

The only way to change the state inside the store
is to dispatch an action on it.

3 functions associated with Store:

  1. createStore() – creates a store
  2. dispatch(action) – changes the state
  3. getState() – retrieves the current state of the store



store.js

import { applyMiddleware, createStore } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
import { composeWithDevTools } from 'redux-devtools-extension';

const store = createStore(
  reducers,
  composeWithDevTools(applyMiddleware(thunk)),
);

export default store;

To make the Redux store available to the rest of your app,
wrap the app with the <Provider /> API provided by React Redux.

Inside src/index.js, wrap App in Provider tag and point it to the store.
* You can also choose to wrap a particular component to make store available only to that component’s children.

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root'),
);
serviceWorker();

import React from "react";
import {connect} from 'react-redux';

const Person = ({name}) => {
    return <>My name is {name}</>;
}

const mapStateToProps = state => (
    // console.log(state), // see what's in state
        {
            name: state.familyReducer.name
        })

const mapDispatchToProps = dispatch => ({})

export default connect(mapStateToProps, mapDispatchToProps)(Person)

Connect function connects your component to the store.
It lets to read values from the Redux store
(and re-read the values when the store updates).

The connect function takes two arguments, both optional:

  1. mapStateToProps
  2. mapDispatchToProps

mapStateToProps
is called every time the store state changes.

It receives the entire store state,
and should return an object of data this component needs.

mapStateToProps Will Not Run if the Store State is the Same

The wrapper component generated by connect subscribes to the Redux store.

Every time an action is dispatched,
it calls store.getState()
and checks to see if lastState === currentState.

If the two state values are identical by reference,
then it will not re-run your mapStateToProps function,
because it assumes that the rest of the store state hasn’t changed either.

The Number of Declared Arguments Affects Behavior
  1. mapStateToProps should always have at least state passed in.

    With just (state), the function runs whenever the root store state object is different.

  2. You may define the function with a second argument, ownProps,
    if your component needs the data from its own props to retrieve data from the store.

    With (state, ownProps),
    it runs any time the store state is different
    and ALSO whenever the wrapper props have changed.

    This means that you should not add the ownProps argument
    unless you actually need to use it,
    or your mapStateToProps function will run more often than it needs to.

Read more >>>

import React from "react";
import { connect } from 'react-redux';

const TodoCreateForm = (props) => {
...
}

const mapStateToProps = (state, ownProps) => ({
  // ... computed data from state and optionally ownProps
  const { visibilityFilter } = state
  const { id } = ownProps
  const todo = getTodoById(state, id)

  // component receives additionally:
  return { todo, visibilityFilter }
})

const mapDispatchToProps = dispatch => ({});

export default connect(mapStateToProps, mapDispatchToProps)(TodoCreateForm);

We will create actions.js and constants.js inside store/actions

Actions

Actions
are payloads of information
which send (or dispatch) data from the application to the store.

Actions are the only source of information for the store.
You send them to the store using store.dispatch().

Actions are plain JavaScript objects.
Other than type, the structure of an action object is really up to the developer.
Read more >>>

It’s a good idea to pass as little data in each action as possible.
(E.g., it’s better to pass item index than the whole item object).

Action type and Action Type Constants

Action type

Actions must have a type property
that indicates the type of action being performed.

Action type
is a string that simply describes the type of an action.
They’re commonly stored as constants or collected in enumerations.

Action type Constants

Types should typically be defined as string constants.

const ADD_TODO = 'ADD_TODO'
////////////////////////////////////
{
  type: ADD_TODO,
  text: 'Build my first Redux app'
}

Once your app is large enough,
you may want to move them into a separate module
(e.g. constants.js or actionTypes.js).

Note: You don’t have to define action type constants in a separate file, or even to define them at all.
For a small project, it might be easier to just use string literals for action types.
However, there are some benefits to explicitly declaring constants in larger codebases.

It allows you to easily find all usages of that constant across the project (if you use an IDE).
It also prevents you from introducing typos, in which case, you will get a ReferenceError immediately.
Read more >>>

constants.js

const constants = {
    ACTION_ONE: "ACTION_ONE",
    ACTION_TWO: "ACTION_TWO",
    ADD_ERROR: "ADD_ERROR",
    CLEAR_ERROR: "CLEAR_ERROR",
    FETCH_SOMETHING: "FETCH_SOMETHING",
    CANCEL_FETCHING: "CANCEL_FETCHING",
}

export default constants;
}



actions.js

/*
 * import action types
 */

import C from './constants'

/*
 * or declare action types
 */
 
export const ADD_TODO = 'ADD_TODO'
export const TOGGLE_TODO = 'TOGGLE_TODO'

/*
 * action creators
 */

export function functionForActionOne(param1, param2, param3=false, param4=false) {
    return {
        type: C.ACTION_ONE,
        payload: {param1, param2, param3, param4}
    }
}

export const functionForAction2 = function(param1) {
    return {
        type: C.ACTION_TWO,
        payload: param1
    }
}
...

Action creator

Action creator
is a function that creates and returns an action.

In Redux, action creators simply return an action.
This makes them portable and easy to test.

function addTodo(text) {
  return {
    type: ADD_TODO,
    text
  }
}

Dispatching Actions

In traditional Flux, action creators often trigger a dispatch when invoked, like so:

function addTodoWithDispatch(text) {
  const action = {
    type: ADD_TODO,
    text
  }
  dispatch(action)
}

In Redux this is not the case.
To actually initiate a dispatch in redux:

  1. Pass the result to the dispatch() function:
    dispatch(addTodo(text))
    dispatch(completeTodo(index))
    
  2. Create a bound action creator that automatically dispatches:
    const boundAddTodo = text => dispatch(addTodo(text))
    const boundCompleteTodo = index => dispatch(completeTodo(index))
    

    Now you’ll be able to call them directly:

    boundAddTodo(text)
    boundCompleteTodo(index)
    
  3. export function todoAdd(todoName) { // action creator
        return dispatch =>
            dispatch({
                type: 'TODO_CREATE', // action type
                payload: todoLabel,
            });
    }
    

The dispatch() function can be accessed directly from the store as store.dispatch(),
but more likely you’ll access it using a helper like react-redux’s connect().

You can use bindActionCreators() to automatically bind many action creators to a dispatch() function.

Action creators can also be asynchronous and have side-effects, read more >>>

mapDispatchToProps:
dispatch is a function of the Redux store.
It is used for dispatching actions to the store.
This is the only way to trigger a state change.

Your components never access the store directly – connect does it for you.

mapDispatchToProps can either be a function, or an object.

  1. If it’s a function, it will be called once on component creation.
    It will receive dispatch as an argument,
    and should return an object full of functions
    that use dispatch to dispatch actions.
  2. If it’s an object full of action creators,
    each action creator will be turned into a prop function
    that automatically dispatches its action when called.

Read more >>>

import React from "react";
import { connect } from 'react-redux';
import { todoCreate } from './todoActions';

const TodoCreateForm = (props) => {
...
const addTodo = (e) =>  {
...
}
...
<form onSubmit={addTodo}>

const mapStateToProps = (state, ownProps) => ({
  // ... computed data from state and optionally ownProps
})

const mapDispatchToProps = dispatch => ({
  // ... normally is an object full of action creators
  todoCreate: todoLabel => dispatch(todoCreate(todoLabel)), // Binds on component re-rendering
});

export default connect(mapStateToProps, mapDispatchToProps)(TodoCreateForm);

Leave a Reply or Comment

Your email address will not be published. Required fields are marked *