Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to update the Redux store after Apollo GraphQL query returns

Tags:

I'm fetching a list of data with the graphql HOC provided by react apollo. E.g.:

const fetchList = graphql(   dataListQuery, {     options: ({ listId }) => ({       variables: {         listId,       },     }),     props: ({ data: { loading, dataList } }) => {       return {         loading,         list: dataList,       };     }   } ); 

I'm displaying the list in a controlled radio button group and I need to select one of the items by default. The id of the selected item is kept in the Redux store.

So, the question is how to update the Redux store (i.e. set the selectedItem) after the query successfully returns?

Some options that came to my mind:

Option 1

Should I listen for APOLLO_QUERY_RESULT actions in my Redux reducer? But that is kind of awkward because then I would need to listen to both APOLLO_QUERY_RESULT and APOLLO_QUERY_RESULT_CLIENT if the query already ran before. And also the operationName prop is only present in the APOLLO_QUERY_RESULT action and not in APOLLO_QUERY_RESULT_CLIENT action. So i would need to dissect every APOLLO_QUERY_RESULT_CLIENT action to know where that came from. Isn't there an easy and straight forward way to identify query result actions?

Option 2

Should I dispatch a separate action like SELECT_LIST_ITEM in componentWillReceiveProps e.g (using recompose):

const enhance = compose(   connect(     function mapStateToProps(state) {       return {         selectedItem: getSelectedItem(state),       };     }, {       selectItem, // action creator     }   ),   graphql(     dataListQuery, {       options: ({ listId }) => ({         variables: {           listId,         },       }),       props: ({ data: { loading, dataList } }) => ({         loading,         items: dataList,       }),     }   ),   lifecycle({     componentWillReceiveProps(nextProps) {       const {         loading,         items,         selectedItem,         selectItem,       } = nextProps;       if (!selectedItem && !loading && items && items.length) {         selectItem(items[items.length - 1].id);       }     }   }) ); 

Option 3

Should I make use of the Apollo client directly by injecting it with withApollo and then dispatch my action with client.query(...).then(result => { /* some logic */ selectItem(...)}). But then I would loose all the benefits of the react-apollo integration, so not really an option.

Option 4

Should I not update the Redux store at all after the query returns? Because I could also just implement a selector that returns the selectedItem if it is set and if not it tries to derive it by browsing through the apollo part of the store.

None of my options satisfy me. So, how would I do that right?

like image 514
TheWebweiser Avatar asked Mar 09 '17 11:03

TheWebweiser


People also ask

How do I update store Redux?

The only way to update a state inside a store is to dispatch an action and define a reducer function to perform tasks based on the given actions. Once dispatched, the action goes inside the reducer functions which performs the tasks and return the updated state to the store. This is what Redux is all about.

Is Redux still necessary with Apollo GraphQL?

Apollo is suitable for managing remote data, and 20% of it was managed in a separate Redux Store, hence there is a need to integrate GraphQL and Redux. Apollo GraphQL no longer requires Redux. Apollo Client automatically catches the data and normalizes new data in query responses and after mutation.

How do you use Apollo and Redux together?

If you want to use your Redux and Apollo state in a component, you need to use both graphql from react-apollo and connect from react-redux. This will let you better track the different events that happen in your app, and how your client and server side data changes interleave.

How do you update the Apollo client cache after a mutation?

If a mutation updates a single existing entity, Apollo Client can automatically update that entity's value in its cache when the mutation returns. To do so, the mutation must return the id of the modified entity, along with the values of the fields that were modified.


2 Answers

I would do something similar to Option 2, but put the life cycle methods into the actual Component. This way the business logic in the life cycle will be separated from the props inherited from Container.

So something like this:

class yourComponent extends Component{     componentWillReceiveProps(nextProps) {       const {         loading,         items,         selectedItem,         selectItem,       } = nextProps;       if (!selectedItem && !loading && items && items.length) {         selectItem(items[items.length - 1].id);       }     }   render(){...} }  // Connect redux and graphQL to the Component const yourComponentWithGraphQL = graphql(...)(yourComponent); export default connect(mapStateToProps, mapDispatchToProps)(yourComponentWithGraphQL) 
like image 66
C.Lee Avatar answered Sep 20 '22 17:09

C.Lee


I would listen to changes in componentDidUpdate and when they happened dispatch an action that will set selectedItem in Redux store

componentDidUpdate(prevProps, prevState) {      if (this.props.data !== prevProps.data) {         dispatch some action that set whatever you need to set     } } 
like image 36
Vladislav Sorokin Avatar answered Sep 20 '22 17:09

Vladislav Sorokin