React-apollo: Best approach to cherry pick data from query into redux-store

Created on 7 Dec 2016  路  9Comments  路  Source: apollographql/react-apollo

The documentation is not yet super clear on how to interact between Apollo Queries and Redux and what the best approaches are to solve common issues.

For example our current hurdle: We are loading the currentUser from GraphQL. The user is authenticated already using SAML + session cookie. The query returns a user profile with an array of roles the user could switch between e.g. admin, editor, manager, ...

On every load of the initial application we load the user profile using a Authorization component. What we are now trying to accomplish is to set the first role from the list inside the profile as the currentRole in our redux store using our reducers. The user should later be able to switch between these roles at runtime.

We are currently using this inside the props mapper in our graphlql connector. It works, but it does not seem to be the most elegant solution to solve this.

Relevant code:

const mapStateToProps = (state) => ({
  currentRole: getActiveCurrentUserRole(state)
})

const mapDispatchToProps = (dispatch) => ({
  setActiveUserRole: (role) => dispatch(setActiveUserRole(role))
})

const AuthorizationWithCurrentUser = graphql(CurrentUserQuery, {
  props: ({ ownProps, data }) => {

    // HERE: How to solve this part
    var currentUser = get(data, "user.currentUser")
    if (currentUser) {
      ownProps.setActiveUserRole(currentUser.roles[0])
    }

    return {
      loading: data.loading,
      error: data.error,
      user: currentUser
    }
  }
})(Authorization)

export default connect(mapStateToProps, mapDispatchToProps)(AuthorizationWithCurrentUser)

We are wondering how other people are solving such cases.

Generally I figure it does not make sense to sync data from Apollo to Redux. But it seems to make sense for interactive client side data e.g. switching roles which are pure client side operations.

Version

Most helpful comment

qq 20161217113234

@helfer I draw a picture, there are two IDs will be dispatch to Redux when user click the <Link />, they are districtID and siteID that used as later query's variable.

But if I want to keep the first ID (companyID) in Redux, immediately when Apollo's loading === false, what should I do?

All 9 comments

Is the idea here to default to the user's first role when the user logs in, and then allow them to choose a different role later?

This to me seems like an imperative operation that should happen only at the time they login or at app startup. So I wouldn't necessarily do it in a graphql wrapper as that's a more declarative operation that could happen at any time (e.g. when data changes).

I am wondering about a similar thing ... basically I want to be able to read an object from the apollo redux store.

ATM, instead of putting user from query into redux store, I am putting it into context.

In my AppContainer I query the viewer

const GET_VIEWER = gql`
  query viewer {
    viewer {
      id username first_name last_name email profile_completed
      role { id name }
      permissions
    }
  }`

Then put it into context

static childContextTypes = {
  viewer: PropTypes.object,
}

getChildContext() {
  const { data: { viewer } } = this.props
  return {
    viewer: merge({}, viewer, {
      authenticated: !!viewer,
      logout: this.logout,
      hasRole: this.hasRole,
      isAllowed: this.isAllowed,
    }),
  }
}

I need to do this:

Load site id list from graphql, map them to buttons -> user click button switch to another page -> load site data, base on current site's id

So site's id should be inside Redux. Now I can put data into redux when a user clicks the button. But there still being some data don't have their button, so I have no good idea about how to dispatch them.

Wonder if this.props.data can contains a Promise, but there still being no place can we wait for this promise's resolve...

@linonetwo I'm not 100% sure what your question is. What prevents you from keeping the id of the page to display in Redux and then switching to another page when you dispatch that action?

I'm particularly confused about this sentence:

But there still being some data don't have their button, so I have no good idea about how to dispatch them.

qq 20161217113234

@helfer I draw a picture, there are two IDs will be dispatch to Redux when user click the <Link />, they are districtID and siteID that used as later query's variable.

But if I want to keep the first ID (companyID) in Redux, immediately when Apollo's loading === false, what should I do?

I'm currently doing the same thing likes @swernerx

function mapStateToProps(state) {
  return {
    token: state.auth.token.token,
  };
}

function mapDispatchToProps(dispatch) {
  return bindActionCreators({ setCompanyID }, dispatch);
}

const queryConfig = {
  options: ({ token }) => ({
    variables: { token }
  }),
  props: ({ ownProps: { setCompanyID }, data: { refetch, loading, Entity, error } }) =>
    loading || error ? ({ loading, error }) : {
      setCompanyID(Entity.id); // <- Here <---------------------------------------
      return {
        loading,
        refetch,
        company: Entity
      }
    }
};

@connect(mapStateToProps, mapDispatchToProps)
@graphql(DistrictListData, queryConfig)
export default class XXX extends Component { }

I think it's better to re-query as more data as possible, don't store them into Redux manually, since apollo-client may take care of the cache.

But there may be some use case that we need to persist data into local storage, thus need to wait for Apollo's loading === false. Is doing such a job inside queryConfig a good choice?

Seems @Cram974 is exploring something interesting: https://github.com/apollostack/apollo-client/issues/1072 @swernerx please take a look at it.

This should really be handled by other application code and tied into React's lifcycle events. I don't want RA to expose any redux specific things since we one day want stores to be pluggable so AC / RA could not even have redux

Was this page helpful?
0 / 5 - 0 ratings