Next.js: how to transfer values between redux and apollo-client ?

Created on 14 May 2017  路  10Comments  路  Source: vercel/next.js

Hi

looking at the example with-redux-and-apollo, I can't figure out how to copy a store's value to props such that it is passed to a child component performing a graphql query. Here's my current approach. I would greatly appreciate any hint, since using { connect } + MapStateToProps approach from 'react-redux' cannot access the store.

./lib/store.js

import Immutable from 'immutable'
const initialState = Immutable.fromJS({
    keyword: 'I want to query this sentence'
})
export default {
  search: (state = initialState, { type, payload }) => {
    switch (type) {
       default:
        return state
    }
}

./page/search.js

import SearchResult from '../containers/SearchResult'
import withData from '../lib/withData'
import App from '../components/App'

export default withData((props) => (
  <div>
    <App>
      <SearchResult keyword={'I DON'T KNOW HOW TO PASS REDUX-STATE HERE'}/>
        </App>
  </div>
))

./containers/SearchResult.js

import withData from '../lib/withData'
import { gql, graphql} from 'react-apollo';
import SearchResult from '../components/Search'

const search = gql`
  query search($keyword: String!, $size: Int!) {
    search(keyword: $keyword, size: $size){
      title
    }
}
`;

export default withData(graphql(search, {
  options: ({ keyword }) => ({
    variables: {
      keyword: { keyword },
      size: 10
    }
  }),
  props: ({ data }) => ({ data })
})(SearchResult))

Most helpful comment

@theednaffattack here is my quick code (not tested) that might help
Note: the rest are the same as @timneutkens's examples and you may have add your reducers and actions

import React from 'react'
// GQL
import { gql, graphql } from 'react-apollo'
// Redux
import { connect, compose } from 'react-redux'
import { bindActionCreators } from 'redux'
// Redux actions if needed
import { doReduxStuff } from '../../redux/filterActions'

class reactClass extends React.Component {

  constructor (props) {
    super(props)
  }

  onClick = (e) => {
    e.preventDefault()
    // dispatch a Redux action
    this.props.action.doReduxStuff({params: 'test'})
  }

  render () {
    // Note: no GQL code here for simplification
    return (
      <div>
        <h1>Data from Redux: {this.props.yourData}</h1>
        <button onClick={this.onClick}>Test Redux</button>
      </div>
    )
  }
}

// You may need to pay attention here
const reduxWrapper = connect(
  // I think this is what you are looking for
  state => ({
    yourData: state.yourData
  }),
  // You can also map dispatch to props
  dispatch => ({
    actions: {
      doReduxStuff: bindActionCreators(doReduxStuff, dispatch),
    }
  }))

// Apollo!
const query = gql`
  query users($id: Int!) {
    users(id: $id) {
      id
      name
    }
  }
`

const gqlWrapper = graphql(query, {
  options: (ownProps) => ({
    variables: {
      id: ownProps.id,
    }
  }),
})

// `compose` makes wrapping component much easier and cleaner
export default compose(
  reduxWrapper,
  gqlWrapper,
)(reactClass)

All 10 comments

@jpabbuehl I'd modify withData.js with

...await (Component.getInitialProps ? Component.getInitialProps({...ctx, store}) : {})

Notice that I added store to the parameters. Now in your search.js you can access Redux store to get the data like this.

static async getInitialProps ({ store, isServer }) {
    await store.dispatch(yourDesiredAction())

And to access the any Redux's state in your search.js page

export default withData(connect((state) => ({state}), mapDispatchToPropsIfYouWant)(SearchPage));

Not sure if this is the correct way but this works for me.

@tsupol
Thanks for your example. I tried the code below in /lib/withData.js with no joy. Any chance you have a handy repo I can peek at? I've been struggling with this for two days now and it's time to seek help from more senior devs.

static async getInitialProps (ctx, store) {
      let serverState = {}

      // Evaluate the composed component's getInitialProps()
      let composedInitialProps = {}
      if (ComposedComponent.getInitialProps) {
        composedInitialProps = await ComposedComponent.getInitialProps({...ctx, store})
      }
...

@timneutkens:
I'm trying to get the exact example you link to working and I still can't mapStateToProps successfully. I'm sure it's simple, but I'm definitely missing how. I can get my dispatches working by wrapping connect (that's react-redux 'connect', not apollo), along with mapDispatchToProps but not state.

The Apollo examples detailing how to wire Apollo and Redux together are great but it's been difficult to adapt them to work with handling the composed component (which is an awesome implementation) in withData.js

@theednaffattack here is my quick code (not tested) that might help
Note: the rest are the same as @timneutkens's examples and you may have add your reducers and actions

import React from 'react'
// GQL
import { gql, graphql } from 'react-apollo'
// Redux
import { connect, compose } from 'react-redux'
import { bindActionCreators } from 'redux'
// Redux actions if needed
import { doReduxStuff } from '../../redux/filterActions'

class reactClass extends React.Component {

  constructor (props) {
    super(props)
  }

  onClick = (e) => {
    e.preventDefault()
    // dispatch a Redux action
    this.props.action.doReduxStuff({params: 'test'})
  }

  render () {
    // Note: no GQL code here for simplification
    return (
      <div>
        <h1>Data from Redux: {this.props.yourData}</h1>
        <button onClick={this.onClick}>Test Redux</button>
      </div>
    )
  }
}

// You may need to pay attention here
const reduxWrapper = connect(
  // I think this is what you are looking for
  state => ({
    yourData: state.yourData
  }),
  // You can also map dispatch to props
  dispatch => ({
    actions: {
      doReduxStuff: bindActionCreators(doReduxStuff, dispatch),
    }
  }))

// Apollo!
const query = gql`
  query users($id: Int!) {
    users(id: $id) {
      id
      name
    }
  }
`

const gqlWrapper = graphql(query, {
  options: (ownProps) => ({
    variables: {
      id: ownProps.id,
    }
  }),
})

// `compose` makes wrapping component much easier and cleaner
export default compose(
  reduxWrapper,
  gqlWrapper,
)(reactClass)

@tsupol
You are the man (unless you are a woman, in which case you are supreme)!
I was actually just about to message that I'd found them (they were there under 'props.state'), but I really appreciate this example anyway as I don't know any devs getting into Apollo. Seeing how others approach this issue is a huge help to my personal development.

@tsupol
Thank you very much, I ll give a try.
With the provided example, I was just able to use them both in parallel, without exchange.

@tsupol is this only working for react-apollo prior to their latest 2.0.x release or also for the latest one?

@natterstefan I think it should work also with Apollo 2, but do you really need Redux?

@johnunclesam hello, actually the answer is no. 9 days ago I was not aware of how to use Apollo 2.0 properly, but not I have a much better picture in my head. Also thanks to these two (link1 and link2) links and the people who participated in each of them. thanks though for your comment.

@tsupol Thanks!
Be careful though. compose is imported from redux, not from react-redux

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jesselee34 picture jesselee34  路  3Comments

olifante picture olifante  路  3Comments

pie6k picture pie6k  路  3Comments

flybayer picture flybayer  路  3Comments

irrigator picture irrigator  路  3Comments