1
votes

I've got a React app that uses Redux for some in-app state management and Apollo for fetching data from a server. In my network tab, my graphql queries are succeeding and the response is what I expect, but when I try to reference the data in the componentDidMount lifecycle of the React Component, the data isn't there and the loading state is 'true'.

If I move my code to a different lifecycle function, like render(), the data does appear, but I need it to work in componentDidMount. I'm new to Apollo.

import React, { Component } from "react";
import PropTypes from "prop-types";
import { connect } from "react-redux";

import SdkMap from "@boundlessgeo/sdk/components/map";
import SdkZoomControl from "@boundlessgeo/sdk/components/map/zoom-control";

import * as mapActions from "@boundlessgeo/sdk/actions/map";

import { graphql } from "react-apollo";
import gql from "graphql-tag";

function mapStateToProps(state) {
  return {
    map: state.map
  };
}

class Map extends Component {
  static contextTypes = {
    store: PropTypes.object.isRequired
  };

  componentDidMount() {
    const store = this.context.store;
    store.dispatch(mapActions.setView([-95.7129, 37.0902], 3));

    /* ADD SITES LAYER */
    store.dispatch(
      mapActions.addSource("sites_src", {
        type: "geojson",
        data: {
          type: "FeatureCollection",
          features: []
        }
      })
    );

    store.dispatch(
      mapActions.addLayer({
        id: "sites",
        source: "sites_src",
        type: "circle",
        paint: {
          "circle-radius": 3,
          "circle-color": "blue",
          "circle-stroke-color": "white"
        }
      })
    );
    
    console.log(this.props.data);  //response doesn't include query fields
    if (this.props.data.allSites) {
      let sites = this.props.data.allSites.edges;

      for (let i = 0; i < sites.length; i++) {
        let site = sites[i].node;
        let geojson = site.geojson;
        if (geojson) {
          console.log(site);
          const feature = {
            type: "Feature",
            geometry: geojson,
            properties: {
              id: site.id
            }
          };
          store.dispatch(mapActions.addFeatures("sites_src", feature));
        }
      }
    }
  }

  render() {
    const store = this.context.store;

    return (
      <SdkMap store={store} >
        <SdkZoomControl />
      </SdkMap>
    );
  }
}

const query = graphql(
  gql`
    query {
      allSites {
        edges {
          node {
            id
            projectId
            location
            areaAcres
            geojson
          }
        }
      }
    }
  `
);

const MapWithRedux = connect(mapStateToProps)(Map);
const MapWithApollo = query(MapWithRedux);

export default MapWithApollo;
1

1 Answers

1
votes

First of all there is no need to access this.context by yourself. This is an anti-pattern. Always use connect(). If you need parts of your state in your component use mapStateToProps. If you want to dispatch actions from your component use mapDispatchToProps to pass functions into it that do the dispatching for you. This is the second parameter that connect() accepts.

Also there is no reason to pass down the store to child components because you can connect every component individually that needs anything from the store.

That being said your problem is that fetching data is asynchronous and your request is probably not completed when componentDidMount() is called. So the information that loading is true just means, that your fetch did not finish yet. Either you display that to the user by e.g. showing some kind of spinner or you fetch the required data before you render your component.