Combining Redux & Apollo Containers

Redux and Apollo are popular implementations of Flux architecture and GraphQL respectively. A common pattern is to make a GraphQL query with data from the Redux state. If the state value changes, Apollo should re-run the query and the new data should be reflected in the UI.

As an example, let’s consider a restaurant website with a rotating menu populated from the server; when a visitor selects a date, the menu should update.

First, let’s create the dispatcher and pass it to the contained component.

function mapDispatchToProps(dispatch: Dispatch): DispatchProps {
  return {
    selectDate: (date) => {
      dispatch({
        type: SELECT_MENU_DATE_ACTION_TYPE,
        menuDate: date
      });
    }
  };
}

Update the state with the new menuDate when the contained component dispatches the action.

export function updateState(state: State, action: MenuAction): State {
  switch (action.type) {
    case SELECT_MENU_DATE_ACTION_TYPE:
      return {
        ...state,
        menuDate: action.menuDate
      };
    default:
      return state;
  }
}

Now that the Redux state is updated, we need to use the new menuDate in our GraphQL query.

function mapStateToProps(state: State): StateProps {
  return {
    date: state.menuDate
  }
}

const connectRedux = connect(mapStateToProps);

Set up the GraphQL query to take in a variable, $date.

const query = gql`
  query Menu($date: String!) {
    menu(date: $date) {
      items {
        name
        priceInCents
      }
    }
  }
`;

Map the menuDate from the Redux state to the $date GraphQL variable.

function mapOwnPropsToOptions(ownProps: ApolloOwnProps) {
  return {
    variables: {
      date: ownProps.date.toISOString()
    }
  };
}

Then connect the query with the map from the query result to the contained component’s props, MenuProps.

function mapQueryResultToContainedProps(opts: OptionProps<ApolloOwnProps, ApolloQueryResult>): MenuProps {
  if (!opts.data || opts.data.loading || !opts.data.menu) {
    return {
      items: []
    };
  }
  return {
    items: opts.data.menu.items
  };
}

const connectApollo = graphql<any, ApolloQueryResult, { date: string }, MenuProps>(
  query,
  {
    options: mapOwnPropsToOptions,
    props: mapQueryResultToContainedProps
  }
);

Now we can feed the Redux output into Apollo, and then the final output to the contained component.

export const Menu = connectRedux(connectApollo(MenuComponent));

At this point, you should be able to see the Redux state changes triggering Apollo queries as expected. See this GitHub project for the complete code example.