1
votes

I've been researching this for the past couple hours, and I am now extremely close to solving this. Everything seems to be working now, but I do not have this.props.mutate in my component that has the Apollo HOC wrapping it.

I wired Apollo up as a reducer, so in my component, I would expect to see this.props.apollo.mutate available, but it's not there.

This is all that seems to be provided currently:

console.log(this.props.apollo)

{"data":{},"optimistic":[],"reducerError":null}

Here is how it is hooked up:

./src/apolloClient.js

import { AsyncStorage } from 'react-native'
import { ApolloClient, createNetworkInterface } from 'react-apollo'

const networkInterface = createNetworkInterface({
    uri: 'http://localhost:8000/graphql',
    opts: {
        credentials: 'same-origin',
        mode: 'cors'
    }
})

// networkInterface is half baked as I haven't got this far yet
networkInterface.use([{
    async applyMiddleware(req, next) {
        if (!req.options.headers) req.options.headers = {}
        const token = await AsyncStorage.getItem('token')
        req.options.headers.authorization = token ? token : null
        next()
    }
}])

const client = new ApolloClient({
    networkInterface,
    dataIdFromObject: (o) => o.id
})

export default client

./src/reducers.js

import client from './apolloClient'

export default combineReducers({
    apollo: client.reducer(),
    nav: navReducer,
    signup: signupReducer,
    auth: loginReducer,
})

./src/App.js

import store from './store'
import client from './apolloClient'

const Root = () => {
    return (
        <ApolloProvider store={store} client={client}>
            <RootNavigationStack />
        </ApolloProvider>
    )
}

export default Root

Oh, and here's the bottom of my Login component (also fairly half-baked):

export default compose(
    connect(mapStateToProps, {
        initiateLogin,
        toggleRememberMe
    }),
    withFormik({
        validate,
        validateOnBlur: true,
        validateOnChange: true,
        handleSubmit: ({ tel, password }, { props }) => {
            props.apollo.mutate({
                    variables: { tel, password }
                })
                .then((res) => {
                    alert(JSON.stringify(res))
                    //const token = res.data.login_mutation.token
                    //this.props.signinUser(token)
                    //client.resetStore()
                })
                .catch((err) => {
                    alert(JSON.stringify(err))
                    //this.props.authError(err)
                })
            //props.initiateLogin({ tel, password })
        }
    }),
    graphql(LOGIN_MUTATION, { options: { fetchPolicy: 'network-only' } })
)(LoginForm)

It feels like I need an action creator and to manually map it to my component. What do I need to do to run this loosely shown mutation LOGIN_MUTATION onSubmit?

I'm currently confused by the fact this.props.apollo has Apollo's data in it, but there is no mutate.

I don't see the solution here: http://dev.apollodata.com/react/mutations.html or maybe I do -- is this what I need to be looking at?

const NewEntryWithData = graphql(submitRepository, {
  props: ({ mutate }) => ({
    submit: (repoFullName) => mutate({ variables: { repoFullName } }),
  }),
})(NewEntry)

I'd like to get it to the point where the component can call the mutation when it needs to. I'd also like it to be available on this.props.something so I can call it from Formik's handleSubmit function, but I am open to suggestions that enable the best declarative scalability.

[edit] Here is the code that I am considering solved:

./src/apolloClient.js

This file was scrapped.

./src/reducers.js

I removed the Apollo reducer and client reference.

./src/App.js

I put the Apollo Client inside the root component. I got this technique from Nader Dabit's Medium post. He illustrates this in a GitHub repo:

Here is how it looks implemented:

const Root = () => {
    const networkInterface = createNetworkInterface({
        uri: 'http://localhost:8000/graphql',
        opts: {
            credentials: 'same-origin',
            mode: 'cors'
        }
    })

    networkInterface.use([{
        async applyMiddleware(req, next) {
            try {
                if (!req.options.headers) req.options.headers = {}
                const token = await AsyncStorage.getItem('token')
                req.options.headers.authorization = token || null
                next()
            } catch (error) {
                next()
            }
        }
    }])

    const client = new ApolloClient({
        networkInterface,
        dataIdFromObject: (o) => o.id
    })

    return (
        <ApolloProvider store={store} client={client}>
            <RootNavigationStack />
        </ApolloProvider>
    )
}

export default Root
1
I think this options.props is what I need. I'm researching it currently.agm1984
To anyone that sees this in the future, please read the React Native docs about AsyncStorage. You should create your own instance of it. Do not use it straight off the package because it has global elements. My code shows it like that, but it's because the code is unfinished.agm1984

1 Answers

4
votes

When you use compose, the order of your HOCs matters. In your code, the props added by your first HOC (connect) are available to all the HOCs after it (withFormik and graphql). The props added by withFormik are only available to graphql. The props added by graphql are available to neither of the other two HOCs (just the component itself).

If you rearrange the order to be compose -> graphql -> withFormik then you should have access to props.mutate inside withFormik.

Additionally, while you can integrate Redux and Apollo, all this does is prevent you from having two separate stores. Passing an existing store to Apollo is not going to change the API for the graphql HOC. That means, regardless of what store you're using, when you correctly use the HOC, you will still get a data prop (or a mutate prop for mutations).

While integrating Apollo with Redux does expose Apollo's store to your application, you should still use Apollo like normal. In most cases, that means using the graphql HOC and utilizing data.props and data.mutate (or whatever you call those props if you pass in a name through options).

If you need to call the Apollo client directly, then use withApollo instead -- this exposes a client prop that you can then use. The apollo prop that connect exposes in your code is just the store used by Apollo -- it's not the actual client, so it will not have methods like mutate available to it. In most cases, though, there's no reason to go with withApollo over graphql.