Skip to content

Instantly share code, notes, and snippets.

@stevekinney
Created November 15, 2017 18:03
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevekinney/da64a2a3cda6ff0fe8d8f1b5303fe8d6 to your computer and use it in GitHub Desktop.
Save stevekinney/da64a2a3cda6ff0fe8d8f1b5303fe8d6 to your computer and use it in GitHub Desktop.
Notes from my Redux live-coding extravaganza just in case I lose my computer or something.

Let's get compose out of the way first, it's just a helper

const makeLouder = (string) => string.toUpperCase();
const repeatThreeTimes = (string) => string.repeat(3);
const embolden = (string) => string.bold();

const makeLouderAndBoldAndRepeatThreeTimes = redux.compose(embolden, repeatThreeTimes, makeLouder);

makeLouderAndBoldAndRepeatThreeTimes('hello');

It just makes a new function that takes a value and runs it through the rest.

const initialState = { result: 0 };

const calculatorReducer = (
  state = initialState,
  action = {}
) => {    
  if (action.type === 'ADD') {
    return { 
      ...state,
      result: state.result + action.value
    };
  }
  return state;
}

Okay, let's create a store!

const store = createStore(calculatorReducer);

Let's subscribe to events!

const subscriber = () => console.log(`Subscriber!' ${store.getState().result}`);

let unsubscribe = store.subscribe(subscriber);

EXERCISE: Implement the other actions!

Answer:

const initialState = { result: 0 };

const calculatorReducer = (
  state = initialState,
  action = {}
) => {    
  if (action.type === 'ADD') {
    return { 
      ...state,
      result: state.result + action.value
    };
  }
  
  if (action.type === 'SUBTRACT') {
    return { 
      ...state,
      result: state.result + action.value
    };
  }
  
  if (action.type === 'MULTIPLY') {
    return { 
      ...state,
      result: state.result + action.value
    };
  }
  
  if (action.type === 'DIVIDE') {
    return { 
      ...state,
      result: state.result + action.value
    };
  }
  return state;
}

So, everything in one store, eh?

const initialErrorMessageState = { message: '' };

let errorMessageReducer = (state = initialErrorMessageState, action) => {
  if (action.type === 'SET_ERROR_MESSAGE') return { message: action.message };
  if (action.type === 'CLEAR_ERROR_MESSAGE') return { message: '' };
  return state;
};

let combinedReducers = redux.combineReducers({ calculatorReducer, errorMessageReducer });

const store = createStore(combinedReducers);

store.dispatch({ 
  type: 'SET_ERROR_MESSAGE',
  message: 'This aggression will not stand.'
});

store.getState();

Oh no, it looks like our object properties are weird.

const store = createStore(
  combineReducers({
    calculator: calculatorReducer,
    error: errorMessageReducer
  })
);

store.getState();

Much better.

What about action creators?

const add = (value) => ({ type: 'ADD', value });

A lot of times you'll see people assign action types to constants.

const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';
const CLEAR_ERROR_MESSAGE = 'CLEAR_ERROR_MESSAGE';

const setError = (message) => ({ type: SET_ERROR_MESSAGE, message});
const clearError = () => ({ type: CLEAR_ERROR_MESSAGE });

If we wanted to do this just for one. We write it by hand:

const bindActionCreator = (action, dispatch) => (...args) => dispatch(action(...args));

Let's say we were using a framework where we passed in an option props, it would be cool to bind them to store.dispatch and just be able to use them, right?

const errorActions = {
  set: setError,
  clear: clearError
}

const errors = bindActionCreators(
  errorActions,
  dispatch
);

If we felt like it, we could even implement an overly simple version ourselves:

const bindActionCreators = (actions, dispatch) => {
  return Object.keys(actions).reduce((boundActions, key) => {
    boundActions[key] = bindActionCreator(actions[key], dispatch);
    return boundActions;
  }, {});
}

Can you implement this with our calculator values?

const add = (value) => ({ type: 'ADD', value });
const subtract = (value) => ({ type: 'SUBTRACT', value });
const multiply = (value) => ({ type: 'MULTIPLY', value });
const divide = (value) => ({ type: 'DIVIDE', value });

const calc = bindActionCreators(
  { add, subtract, multiply, divide },
  store.dispatch
)

Now I can do cool things like…

calc.add(4);

store.getState();

We can also step in the middle… with middleware!

const logger = ({ getState }) => {
  return next => action => {
    console.log('I am about to dispatch:', action)

    const returnValue = next(action)

    console.log('I am done with the reducer', getState())

    return returnValue
  }
}

There are some rules here.

  • Logger gets an object with two methods: dispatch and getState
  • It returns a function that takes the next thing in the chain.
  • And then it gets the action.

So, you get the action, the next piece of middleware, and the about to getState and dispatch.

Very cool.

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