5

I'm building a React front end which allows users to select an "active" query from a list of static queries and flattens to the result to be displayed in a table. What is the best way to pass the GraphQL query from a higher-order component into a nested child component?

Most of the documentation/solutions that I've seen focus on binding a static query with dynamical conditions from component states to a component, which would not work for my purpose as the different static queries have varying fields and query different node types.

What is the best-practice/recommended approach here? I feel like this is not a very unique use case, but I can't seem to find any examples that would do something similar.

I'm using Apollo-Client/Redux as my client-side store.

Below is the rough outline of the component:

class GridViewPage extends React.Component{
  constructor(props, context) {
    super(props, context);
    this.state = {
      activeQuery = ... Stores the selected query ...
    };
  }

  render() {
    return (
      <div className="gridContainer">
        ...Component here allows users to select a query from the active list and saves it/it's ID/Index to the state...

        <Panel collapsible>
        ...Some toolbar components...
        </Panel>
        ...Component here displays the result of the query (Ideally by receiving the query or the result of as a prop?)...
      </div>
    );
  }
}

GridViewPage.propTypes = {
  grids: PropTypes.array.isRequired,
  actions: PropTypes.object.isRequired
};

function mapStateToProps(state, ownProps) {
  return {
      // Receives list of available queries as a prop
      grids: state.grids
  };
}

1 Answer 1

3

Let's take, for example, the following component:

ProfileWithData.js

import React, { Component, PropTypes } from 'react';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

class Profile extends Component { ... }
Profile.propTypes = {
  data: PropTypes.shape({
    loading: PropTypes.bool.isRequired,
    currentUser: PropTypes.object,
  }).isRequired,
};

// We use the gql tag to parse our query string into a query document
const CurrentUserForLayout = gql`
  query CurrentUserForLayout {
    currentUser {
      login
      avatar_url
    }
  }
`;

const ProfileWithData = graphql(CurrentUserForLayout)(Profile);

It would be quite easily wrapping it with a higher order component:

Profile.js

import React, { Component, PropTypes } from 'react';

export class Profile extends Component { ... }
Profile.propTypes = {
  data: PropTypes.shape({
    loading: PropTypes.bool.isRequired,
    currentUser: PropTypes.object,
  }).isRequired,
};

createProfileWithData.js

import React, { Component, PropTypes } from 'react';
import { graphql } from 'react-apollo';
import { Profile } from './Profile'

export default function createProfileWithData(query) => {
  return graphql(query)(Profile);
}

You would then use it like this:

Page.js

import React, { Component, PropTypes } from 'react';
import gql from 'graphql-tag';
import createProfileWithData from './createProfileWithData';

class Page extends Component {  

  renderProfileWithData() {

      const { textQuery } = this.props;
      // Simplest way, though you can call gql as a function too
      const graphQLQuery = gql`${textQuery}`;

      const profileWithDataType = createProfileWithData(graphQLQuery);

      return (
        <profileWithDataType />
      );
  }

  render() {

    return (<div>
                ..
                {this.renderProfileWithData()}
                ..
           </div>)
  }

}

Profile.propTypes = {
  textQuery: PropTypes.string.isRequired,
};

I think you get the point.

Of course, your Profile would not received props.data.currentUser, rather it would be props.data.* depending on the root queries, and you would handle it appropriately depending on the contents.

Note: this was written directly in Stack Overflow, so if you encounter any problems - lmk and I'll fix it.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! Have started to apply this pattern in the project and I think I will get it to work eventually. One immediate error I am having is that const profileWithDataType = createProfileWithData(graphQLQuery); is returning a function type, and therefore not hitting the render()function when nested inside {this.renderProfileWithData()}? This might be a syntax issue on my code, but thought worth mentioning.
Yeah, it might be an issue with JSX. I always forget if that syntax is valid or not. You could also do: ``` return React.createElement(profileWithDataType, {}); ``` I am certain you already solved this by now, so an update would be nice :)
Hey! Actually I ended up skipping createProfileWithData.js and setting the query to the page directly in the Page.js with the equivalent of graphql(query)(Profile) and it works :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.