The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

hettlage (136) [Avatar] Offline
#1
It might be worth discussing the return value of the function returned by asynchronous action creators in Chapter 4. (Unit testing with redux-mock-store seems to require that it isn't a void function but actually returns a promise.)
TrueWill (22) [Avatar] Offline
#2
I agree; I noticed this too. For example fetchTasks() in section 4.1.

https://redux.js.org/docs/advanced/AsyncActions.html#async-action-creators
suggests returning a promise from thunk action creator internal functions, as it's passed as the return value of the dispatch method.

This lets you write code like:

store.dispatch(fetchTasks()).then(doSomething());
Will Faurot (8) [Avatar] Offline
#3
Great suggestion, thanks. We start returning promises in the testing chapter (9) specifically for redux-mock-store, but aren't consistent with it elsewhere.. We're working on a section to cover this around when we introduce async actions, and will look at updating the code.
tempusfugit (144) [Avatar] Offline
#4
The promises can take the shape of ES2017 async functions, also allowing the use of await, e.g.
// src/actions/index.js
import * as api from '../api';

function createTaskSucceeded(task){
  return {
    type: 'CREATE_TASK_SUCCEEDED',
    payload: {
      task
    }
  };
}

export function createTask({title, description, status = 'Unstarted'}) {
  return async dispatch => {
    const { data } = await api.createTask({ title, description, status });
    dispatch(createTaskSucceeded(data));
  };
}

function editTaskSucceeded(task){
  return {
    type: 'EDIT_TASK_SUCCEEDED',
    payload: {
      task
    }
  };
}

function getTaskById(tasks, id) {
  return tasks.find(task => task.id === id);
}

export function editTask(id, params = {}) {
  return async (dispatch, getState) => {
    const task = getTaskById(getState().tasks.tasks, id);
    const editedTask = {...task, ...params};
    const { data } = await api.editTask(id, editedTask);

    dispatch(editTaskSucceeded(data));
  };
}

function fetchTasksSucceeded(tasks){
  return {
    type: 'FETCH_TASKS_SUCCEEDED',
    payload: {
      tasks
    }
  };
}

function fetchTasksStarted() {
  return {
    type: 'FETCH_TASKS_STARTED'
  };
}

function fetchTasksFailed(error) {
  return {
    type: 'FETCH_TASKS_FAILED',
    payload: {
      error
    }
  };
}

export function fetchTasks() {
  return async dispatch => {
    dispatch(fetchTasksStarted());

    try {
      const { data } = await api.fetchTasks();
      // delay for demonstration
      const _delay = await new Promise(r => setTimeout(r, 2000));

      dispatch(fetchTasksSucceeded(data));

    } catch (error) {
      const message = `Oh noes! Unable to fetch tasks! "${error.message}"`;

      dispatch(fetchTasksFailed(message));
    }
  };
}