Skip to content

Instantly share code, notes, and snippets.

@kiok46
Last active May 10, 2021 23:17
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kiok46/eed0dd78719405b1ccad12edeb1139af to your computer and use it in GitHub Desktop.
Save kiok46/eed0dd78719405b1ccad12edeb1139af to your computer and use it in GitHub Desktop.
Action Creator and reducers details and flow

Action Creators and Reducers.

why action creators? We want to minimize the responsibilities of anyone of our components, We want to make a component as simple and as dumb as possible, so all our compenent is going to do is show the UI. whenever our Component is going to do some logical work we would call the action creator.


Component -> renders the internal components -> need to do some action, let's say handle a button click
-> calls the action creator
-> ...

How the flow should work.

User does something -> Redux comes into action(desides whether to update state or not) -> render new stuff.

React: Showing something to the screen and watching for an event. Redux: Keeping track the state of our application and decide how to interprate these actions coming into our application. (Decision making)

Whole process let's say we want to add this to a TextInput so how is the entire process going to look like?

User types something -> call action creator with new text -> action creator returns an action 
-> action sent to all reducers -> reducers calculates new app state
-> state sent to all components -> component rerender with new state -> wait for new change
-> repeat.

continuing with the example of TextInput

    1. create a event handler onChangeText and attach a callback this.onTextChange

and define that function in the same class.

onTextChange = (text) => {

}

^ Using the above syntax spares us to type this.onTextChange.bind(this) in other words it known which this we are talking about.

    1. create a new folder actions
    1. create a new file inside actions folder index.js
export const textChanged = (text) => {
  return {
    type: 'text_changed',
    payload: text
  }
}
    1. import action creator in the file which has our TextInput component and import connect helper from react-redux
import { connect } from 'react-redux';
import textChanged from '../actions';

class TextComponent extends Component {
 ... bla bla bla
}

export default connect(null, textChanged)(TextComponent);

Where are we right now?

user types something -> calls event handler -> calls action creator with new text
-> action creator returns an action -> sends that actions to all the reducers

we haven't created a reducer yet and we need one to catch that action and really do something about it.

    1. create a new folder reducers
    1. create a new file inside the reducers folder TextReducer.js to handle that action.
const INITIAL_STATE = {
  text: ''
};


export default ( state=INITIAL_STATE , action ) => {
  /* We always have the exact same format for every reducer we write, we always have a switch statement
  and depending on the action it will decide what to do with it.
  
  we can never return undefined from a reducer, every time our reducers are call we should have a 
  default state. (just like we had when we didn't used redux and defined the state in the constructor itself.)
  */
  switch (action.type) {
    case:
       // in step 11
      
    default:
      // we will just return the state.
      // return the initial state when nothing.
      return state
  }
}
    1. create a new file inside the reducers folder index.js
import { combineReducers } from 'redux';
import TextReducer from './TextReducer';

export default combineReducers({
  // the keys here are going to be the property of state that we are producing.
  text_reducer: TextReducer
  
});

Now that we have created a reducer and acition creator we need to make sure that our reducer watches for a action of appropriate type.

    1. create a file types.js in actions folder and in that file we create variables to store action types.

one file all the actions type.

export default TEXT_CHANGED = 'text_changed';
    1. move to index.js and import action type we just created and replace that string with our imported variable.
import {
  TEXT_CHANGED
} from './types';

export const textChanged = (text) => {
  return {
    type: TEXT_CHANGED,
    payload: text
  }
}
    1. import that type into our reducer.
import {
  TEXT_CHANGED
} from '../actions/types';

/* remember that TEXT_CHANGED should be defined and should have a value otherwise it will be 
undefined and no error would popup.

and the in the reducer we will have a case of undefined

case undefined:
   return ...
   
which is not what we want.
*/

const INITIAL_STATE = {
  text: ''
};


export default ( state=INITIAL_STATE , action ) => {
  switch (action.type) {
    case TEXT_CHANGED:
       /*
          slice of state (that the reducer last published)  +  action
                         |
                    into the reducer
                         | 
              returns a new slice of state
       
       After our reducer runs, redux looks at the old value of the state and the new one.
       `is newState === oldState?` (matches the object)
       we must return a new object. (have to take care of immutable objects)
       */
       
       // make a new object, take all the properties from the existing state object and throw that into
       // out new object then define the property email, give it a value of action.payload and put it one
       // top of whatever properties we had on the old state object.
       
       // now, since old state object already has a email property so, it will be overwritten with a new
       // value.
       
       return {...state, text: action.payload}
      
    default:
      // we will just return the state.
      // return the initial state when nothing.
      return state
  }
};
    1. Update our callback method and trigger the action creator so that it could update the state.
onTextChange = (text) => {
    this.props.textChanged(text)
}
    1. define a mapStateToProps function in the file containing Component class.

This will be called with out global application state and we should return just the property we care about from that state object.

const mapStateToProps = state => {
  return {
    text: state.text_reducer.text
  }

}

pass it as the first argument to our connect function.

export default connect(mapStateToProps, textChanged)(TextComponent);

    1. pass the value into the component
value={this.props.text}

cool!!

@hellolin324
Copy link

Wow very easy to understand for the lay person, thanks for laying out so well, even though I am using Polymer 3 this is pretty much the same flow.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment