Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call GraphQL outside a component

I have made a bunch of React component calling GraphQL using the Query component and everything is working fine.

In one component I need to have some initial data from the database, but without any visual representation.

I have tried to use the query component but it seems to be triggered only on the render cycle. I have tried to package it into a function and call this function in the component that needs the data. But the code / query is not executed since there's no component to show.

How do I go about getting this data from the database without a component?

I can't find any documentation on how to solve this problem. But I can't be the only one doing this.

Is ApolloConsumer or ApolloProvider the answer to my problems?

I'm working with conferences and sessions. A conference runs over a couple of days and each day has a number of sessions.

What I'm trying to achieve is to render a page with X numbers of tabs one for each day. Each tab represents a day and it shows the number of sessions for the day.

My sessions page:

    import React from 'react';
import FullWidthTabs from '../components/Sessions';
import SessionTab from '../components/SessionTab';
import BwAppBar2 from '../components/BwAppBar2';
import ConferenceDays from '../components/ConferenceDays';


class SessionsPage extends React.Component {

    static async getInitialProps() {
        console.log("GetInitProps SessionsPage");
    }

    render() {
        let a = ConferenceDays();
        return (

                <div>
                    <BwAppBar2 />
                    {a}
                     <FullWidthTabs days={['2018-06-11', '2018-06-12', '2018-06-13']} day1={ < SessionTab conferenceId = "57" day = '2018-06-11' / > } 
                                   day2={ < SessionTab conferenceId = "57" day = '2018-06-12' / > } day3={ < SessionTab conferenceId = "57" day = '2018-06-13' / > }>
                    </FullWidthTabs>
                </div>
                );
        }
}
export default (SessionsPage);

Here the dates have been hardcoded in the page just for testing.

But order to know how many days the conference spans i'll have to find the conference and decide the start and end date and generate all the dates in between:

import React, { Component } from 'react'
import { graphql } from 'react-apollo'
import { Query } from 'react-apollo'
import gql from 'graphql-tag'
import Link from '@material-ui/core/Link';
import { useQuery } from "react-apollo-hooks";

import conferencesQuery from '../queries/conferences'
import { Table, Head, Cell } from './Table'
import ConferenceCard from './ConferenceCard';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
import moment from 'moment';


const CONFERENCE_QUERY = gql`
 query conference($conferenceId : ID!){
      conference(id: $conferenceId){
          title
          start_date
          end_date
     }    
}
`
let index = 0;
let loopDate = 0;
let dates = [];
let conferenceId = 57;

const ConferenceDays = () => (
<Query query={CONFERENCE_QUERY} variables={{conferenceId}}>
    {({ loading, error, data }) => {
                        if (loading)
                            return <div>Fetching</div>
                        if (error)
                            return <div>Error</div>
                        const startDate = moment(data.conference.start_date, 'x');
                        const endDate = moment(data.conference.end_date, 'x');

                        for (loopDate = parseInt(data.conference.start_date);
                                loopDate < parseInt(data.conference.end_date);
                                loopDate += 86400000) {

                            let aDate = moment(loopDate, 'x');
                            dates.push(aDate.format('YYYY-MM-DD').toString());
                        }
                        console.log(dates);
                        return(dates);
                    }}
</Query>);

export default ConferenceDays

But is this approach incorrect?

Would it be more correct to lift the ConferenceDates component up in the hierarchy?

Kim

like image 808
Kim Gabrielsen Avatar asked May 28 '19 11:05

Kim Gabrielsen


3 Answers

You could separate the creation of the ApolloClient to a separate file and use an init function to access the client outside of React components.

import React from 'react';
import {
  ApolloClient,
  HttpLink,
  InMemoryCache,
} from "@apollo/client";

let apolloClient;

const httpLink = new HttpLink({
  uri: "http://localhost:4000/graphql",
  credentials: "same-origin",
});

function createApolloClient() {
  return new ApolloClient({
    link: httpLink,
    cache: new InMemoryCache(),
  });
}

export function initializeApollo() {
  const _apolloClient = apolloClient ?? createApolloClient();
  if (!apolloClient) apolloClient = _apolloClient;

  return _apolloClient;
}

export function useApollo() {
  const store = useMemo(() => initializeApollo(initialState), [initialState]);
  return store;
}

Then you would use this outside components like this:

const client = initializeApollo()
const res = await client.query({
  query: MY_QUERY,
  variables: {},
})

I didn't try this myself, but I think this you an idea on how to go about this and how to access the ApolloClient.

like image 137
slinden Avatar answered Sep 28 '22 06:09

slinden


ApolloClient has a mutate method. You can just import your Apollo client instance and call apolloClient.mutate.

like image 30
ericls Avatar answered Sep 28 '22 05:09

ericls


If you are using functional components, you can use useApolloClient hook in a function as though it is not a hook.

import { useApolloClient, gql } from "@apollo/client";

 MY_QUERY = gql'
   query OUR_QUERY {
     books{
        edges{
           node{
             id
             title
             author
            }
         }
      }
   }
'

const myFunctionalComponent = () => {   // outside function component

    const client = useApolloClient();

    const aNormalFunction = () => {   // please note that this is not a component  
       client.query({
          query: MY_QUERY,
          fetchPolicy: "cache-first"   // select appropriate fetchPolicy
       }).then((data) => {
          console.log(data)   //do whatever you like with the data
       }).catch((err) => {
          console.log(err)
       })
    };

    // just call it as a function whenever you want
    aNormalFunction()    
    
    // you can even call it conditionally which is not possible with useQuery hook
    if (true) {
        aNormalFunction()
    }

    return (
        <p>Hello Hook!</>
    );
};

export default myFunctionalComponent;
like image 31
Si Thu Avatar answered Sep 28 '22 04:09

Si Thu