question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

withMulti results become undefined on terms change

See original GitHub issue

Hi, all is in the title. I setup a Map component that rely on withMulti. The architecture is:

  • The parent component wraps its children with withMulti (as the Datatable does with DatatableContent)
  • The children display results on map
  • There is an intermediate component that manage terms update when the user moves

Terms are correctly updated. But if I move the Map, the marker disappear, as results become undefined (ie graphql props.data[resolverName] is undefined). No query is triggered according to network. A call to refetch fixes but provokes blinking and sounds like a palliative.

However, when the next polling is triggered, the correct data are retrieved. It’s just changing terms that does not behave as expected.

This issue seems to have appeared to other people, but not all the time, so it seems related to how we define the React component and how we use withMulti. I can reproduce even with apollo-client and apollo-react version 2 as well as the current Vulcan version. Maybe smth is failing silently ?

I will try to setup a minimal repro, but in the meantime here is my component code, in case someone understand this issue (or want to setup a Leaflet Map that almost work 😃):

import React, { Component } from 'react';
import { Components, registerComponent, withMulti } from 'meteor/vulcan:core';

// the parent, it creates the component wrapped with `withMulti` and 
// handle the initial render
class GeoSearchPage extends Component {
  render() {
    const { collection, defaultTerms, ...otherProps } = this.props;
    const withMultiOptions = {
      collection,
      terms: defaultTerms
    };
    const GeoSearchPageInnerWithMulti = withMulti(withMultiOptions)(
      Components.GeoSearchPageInner
    );
    return (
      <ViewportProvider
        {...otherProps}
        defaultTerms={defaultTerms}
        C={GeoSearchPageInnerWithMulti}
      />
    );
  }
}

// manage state, is in charge of updating terms and passing to its child,
// which is a component wrapped with withMulti
class ViewportProvider extends Component {
  constructor(props) {
    super(props);
    this.state = {
      currentTerms: props.defaultTerms,
      currentViewport: null
    };
  }
  isValidViewport = viewport => {
    return (
      typeof viewport.center[0] === 'number' &&
      typeof viewport.center[1] === 'number' &&
      viewport.zoom < Infinity
    );
  };
  shouldUpdateTerms = newViewport => {
    const { currentViewport } = this.state;
    if (!this.isValidViewport(newViewport)) return false;
    if (!currentViewport && newViewport) return true;
    return (
      newViewport.center[0] !== currentViewport.center[0] ||
      newViewport.center[1] !== currentViewport.center[1] ||
      newViewport.zoom !== currentViewport.zoom
    );
  };
  computeNewTerms = viewport => {
    const { zoom, center } = viewport;
    const [lat, lng] = center;
    const terms = { ...this.state.currentTerms };
    terms.geoQuery.point.lat = lat;
    terms.geoQuery.point.lng = lng;
    terms.geoQuery.maxDistance = 10000; // TODO compute depending on zoom
    return terms;
  };
  onViewportChanged = viewport => {
    const newTerms =
      this.shouldUpdateTerms(viewport) && this.computeNewTerms(viewport);
    const newViewport = this.isValidViewport(viewport) && viewport;
    let newState = {};
    if (newTerms) newState['currentTerms'] = newTerms;
    if (newViewport) newState['currentViewport'] = newViewport;
    this.setState(newState);
  };

  render() {
    const { C, ...otherProps } = this.props;
    const { currentViewport, currentTerms: terms } = this.state;
    const additionalProps = {
      viewport: currentViewport,
      terms: terms,
      onViewportChanged: this.onViewportChanged
    };
  // C is a component wrapped with `withMulti`, we pass the terms props to it
    return <C {...otherProps} {...additionalProps} />;
  }
}

// some helper, ignore it
const computePositions = (results, latLngField = 'placeLatLng') => {
  return (
    results.filter(r => !!r[latLngField]).map(r => r[latLngField])
      .filter(
        ({ lat, lng }) => typeof lat === 'number' && typeof lng === 'number'
      ).map(({ lat, lng }) => [lat, lng])
  );
};

// inner map, that will be wrapped with withMulti
class GeoSearchPageInner extends Component {
  componentWillReceiveProps(newProps) {
    if (newProps.terms !== this.props.terms) {
       // will work, but the marker still blink as `results` is temporarily undefined
      // refetch should not be necessary I think
      this.props.refetch()
    }
  }
  render() {
    const {
      results = [],
      onViewportChanged,
      ...otherProps
    } = this.props;
    const positions = computePositions(results);
    return (
      <div>
        <Components.LeafletMap
          positions={positions}
          onViewportChanged={onViewportChanged}
          {...otherProps}
        />
      </div>
    );
  }
}

registerComponent({
  name: 'GeoSearchPageInner',
  component: GeoSearchPageInner
});
registerComponent({ name: 'GeoSearchPage', component: GeoSearchPage });

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:11 (11 by maintainers)

github_iconTop GitHub Comments

1reaction
Apollinairecommented, Oct 29, 2018

i think it can be done here, it should not break things: the only thing we are doing is taking some work out of the queryReducer and doing it above, the same functions will be ran with the same arguments, just a bit earlier in the component tree. I’m doing the PR, this way we can test it and see if we keep it or not

0reactions
SachaGcommented, Oct 29, 2018

The reducer option config seems deprecated so we will have to rewrite the withMulti hoc

Yes, that’s already done on the Apollo 2 branch.

Read more comments on GitHub >

github_iconTop Results From Across the Web

React hook form setValue returns undefined with multi select ...
Upon change the value seems to successfully change, but when I submit the form the select value is undefined. I have input fields...
Read more >
Issue with Multi row variable set - ServiceNow Community
I copied MRVS from Dev instance to test environment thru update set, it is working fine in Dev but not in test environment....
Read more >
CSS ordering from shared chunk not deterministic with multi ...
In my repo, I set it up so that anything in shared_modules is split out into a separate chunk, but have the entry...
Read more >
Procedural language | BigQuery - Google Cloud
If an EXECUTE IMMEDIATE statement returns results, then those results become the result of the entire statement and any appropriate system variables are ......
Read more >
Safari Technology Preview Release Notes - Apple Developer
Changed arguments.callee to become ThrowTypeError if the function has a complex-parameter-list (spec-term); Changed BigInt constructor to be constructible ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found