简体   繁体   中英

imperative logic on component that renders non-React components (mostly)

I have a React component (let's call it A ) that mainly renders a non-React component (a GIS map) and will on certain occasions display some modal dialogs. These dialogs are the only proper React components that the A component renders and are controlled by A 's local State . The GIS map draws some features (polygons) which the component receives through its props , but the logic to draw these features is purely imperative as the GIS map cannot be controlled by React.

So I have the following approach:

componentDidUpdate(prevProps, prevState) {
    // check for changes between prevProps and this.props and
    // imperatively draw new polygons on map or remove polygons etc.
}

This works. However it occurred to me that when a new polygon arrives via props , the render is not necessary. This is because drawing the polygons on the map is done in an imperative way via direct DOM manipulation (through API calls to the GIS library). As such, I wrote a shouldComponentUpdate as follows:

shouldComponentUpdate(nextProps, nextState) {
   return !isEqual(nextState, this.state); // isEqual from lodash doing a deep comparison
}

The idea is that I only re-render my component for local state changes (which control a couple of modal dialogs) since these are the only React-controlled parts of the component and require render to be called. The rest of the changes (that affect non-React components) I pick up in componentDidUpdate and enforce imperatively (that was the plan at least).

The problem with the new approach is that if shouldComponentUpdate returns false , then componentDidUpdate is not called at all. So the only way to get hold of props changes that trigger imperative logic is via componentWillReceiveProps which is however .

Any thoughts? Is my approach flawed in a deeper level and/or how to deal with this connundrum? Is what I am trying to accomplish achievable only using a functional component approach (as has been suggested by a comment) and can I achieve it with class components as well?

Seems to be an interesting problem. How are props mapped to your GIS-Widget? React does allow nested elements that are not under react rule ( docu ).

I would ditch the old class based lifecycle functions and implement a functional component with useEffect and useRef hook.

Does the following code somehow resemble your current issue? Let me know what I got wrong and we can make it work.

import React, { useRef, useEffect, useState } from 'react';
import gismap from 'gis-map';

const MyGISWrapper = ({ streets, waterways }) => {
  const map = useRef();

  // only once, on first render
  useEffect( () => { if (map.current) gismap('init', map.current); }, []);

  // update polygons when props change
  useEffect( () => {
    if (map.current) map.current.gismap('update', streets, waterways);
  }, [streets, waterways]);

  // some example modal
  const [showConfirmation, setShowConfirmation] = useState(false);
  const openConfirmation = () => setShowConfirmation(true);
  const closeConfirmation = () => setShowConfirmation(false);

  return (
    <div className="container">
      <div className="gis-map-root" ref={map} />
      <div className="modal-popup-area">
        { showConfirmation && <Confirmation onClose={closeConfirmation} /> }
      </div>
      <button onClick={openConfirmation} />
    </div>
  );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM