Search code examples
clojurescriptreagent

React HOC wrapping with Clojurescript and Reagent


I'm trying re-use the components from react-google-maps and implement the simple map example from the doc: https://tomchentw.github.io/react-google-maps/basics/simple-map

However, I'm a blocked by the withGoogleMap Higher-Order Component (HOC) that wraps the GoogleMap component. I have tried to adapt the classes with Reagent and use them as follow:

(def GoogleMap (adapt-react-class js/ReactGoogleMaps.GoogleMap))
(def withGoogleMap (adapt-react-class js/ReactGoogleMaps.withGoogleMap))

(defn Map [props]
  [withGoogleMap
   [GoogleMap props]])

in lieu of the following Javascript:

const Map = withGoogleMap(props => (
  <GoogleMap
     {... props}
  >
  </GoogleMap>
));

Without success. (I get the following error withGoogleMap(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.).


Solution

  • The withGoogleMap HOC is a function that should be called with the wrapped component. To implement the simple map example, you also need to provide props to the GoogleMap component to be wrapped. This can be achieved by adapting the component to reagent with adapt-react-class, implementing the CLJS component, and "converting back" with reactify-component before calling the HOC.

    (ns simple-map.views
      (:require [reagent.core :refer [adapt-react-class 
                                      create-element 
                                      reactify-component]]
                react-google-maps))
    
    (defn my-google-map []
      (let [google-map (adapt-react-class react-google-maps/GoogleMap)]
        [google-map {:defaultZoom   18
                     :defaultCenter {:lat 61.4481532
                                     :lng 23.8608644}}]))
    
    (defn MyMap [props]
      (let [m (-> my-google-map
                  reactify-component
                  react-google-maps/withGoogleMap
                  adapt-react-class)]
        [m props]))
    
    (defn main-panel []
      (let [container-element (create-element "div" #js{:style (clj->js {:height "768px" :width "1024px"})})
            map-element       (create-element "div" #js{:style (clj->js {:height "768px" :width "1024px"})})]
        (fn []
          [:div
           [MyMap {:containerElement container-element
                   :mapElement       map-element}]])))
    

    Note that I am using the experimental :npm-deps support to require react-google-maps.