Skip to content

Instantly share code, notes, and snippets.

@phyllisstein
Last active July 12, 2022 15:03
Show Gist options
  • Save phyllisstein/1e56ec9fc7945f934751df87aa2dd121 to your computer and use it in GitHub Desktop.
Save phyllisstein/1e56ec9fc7945f934751df87aa2dd121 to your computer and use it in GitHub Desktop.
Two patterns for sharing MapStack UI.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ⭑ Approach Two ⭑ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// ~~~~~~~~~~~~~~~~~~~~~ Component Components Component ~~~~~~~~~~~~~~~~~~~~~ //
/**
* Our package exposes a number of React components purpose-dedicated to
* building a map; the downstream app composes the React components we expose.
* The Unison component, not our code, would call ReactDOM::render. This is the
* pattern one normally sees with React libraries.
*/
import { MapsStack, Map, Pin } from '@voxmedia/maps-ui'
import ReactDOM from 'react-dom'
ReactDOM.render(
<MapStack>
<Map>
<Pin id='7c75fa4c-edf0-4dab-9d2c-aa8c9d0afd9d' />
</Map>
</MapStack>,
document.querySelector('.container'),
)
/**
* When the state of the page changes, the downstream app changes the data on
* our React components.
*/
<Pin focused={ !isFocusedNow } id='7c75fa4c-edf0-4dab-9d2c-aa8c9d0afd9d' />
/**
* React doesn't need to own the entire page to handle state and offer hooks for
* callbacks.
*/
<MapProvider data={{ venueID: '7c75fa4c-edf0-4dab-9d2c-aa8c9d0afd9d', mapKind: 'territory' }}>
<MapStack>
<Map onMoreMaps={ () => {} }>
<Pin id='7c75fa4c-edf0-4dab-9d2c-aa8c9d0afd9d' />
</Map>
</MapStack>
</MapProvider>
/**
* Conceivably, this opens up a larger surface for the downstream app to break
* something. It also asks downstream teams to work in JSX which is not always
* familiar. Nor are React's declarative design idioms; nor its funkier new
* features, like hooks.
*
* HOWEVER. We also vastly widen the possibilities for our team and others to
* play with and iterate on the map. This is broadly more good than the risk of
* bugs is bad. Shifting to a declarative pattern for state changes feels
* appetizing after struggling for only a few days with imperative updates in
* Backbone. And dropping this fuzzy intermediary message broker (that is, the
* wrapper class) keeps our focus on the component layer---a piece we were going
* to bulid anyway.
*
* You may have surmised that I talked myself into this one as I typed the
* differences out loud. I did. Disagree with me!
*/
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Approach One: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Wrapper Class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
/**
* Our package exports a single wrapper class. It wraps all the functionality of
* mapping application. We new it up in a Unison component, passing a selector,
* and hand over rendering the map.
*
* (This is not all that different from how Backbone works. We'll get back to
* that in a bit.)
*/
import MapStack from '@voxmedia/maps-ui';
const mapStack = new MapStack('.container')
/**
* When the state of the page changes, we call methods on the wrapper instance
* to true up the rendered map.
*/
// View#onScroll...
mapStack.focusPin('7c75fa4c-edf0-4dab-9d2c-aa8c9d0afd9d')
// View#onResize...
mapStack.setSize(window.innerWidth, window.innerHeight)
/**
* The wrapper is an event emitter. The Unison component listens for its events
* and shunts them to the appropriate bits of SBN.
*/
mapStack.on('affiliateClick', () => {
this.analytics.moneyPixel();
})
/**
* Hiding most of the view's implementation could make it easier to iterate on
* the component layer faster. As long as the SBN page knows MapStack#focusPin
* will focus on a pin, it doesn't need to know how the pin gets focused.
* Closing over the UI also means we keep tighter control over the look and feel
* of the map.
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment