Hey :)
Basically reposting here to invite mapbox contributors at the table, as suggested
by @alex3165, and @ibgreen here...
I think if that kind of discussion happen, we could keep this issue open here as a main, and close others.
Both MapboxGL.js and React are kinda 4 years old. There are currently 3 different, active and maintained, libraries out there trying to implement a React friendly API for Mapbox. While each of them is pretty great and hard work, each of them lack features. When coming from a raw MapboxGL.js background, new to react, this is pretty critical and hard to make a reliable choice. I guess coming from a React background and discovering MapboxGL and making the same choice is also hard.
This seems pretty much complicated, as governance, paradygms, workflows and many stuff are different in each of the repos. But it also feels like there is so much relevant and valuable thing to do with all that hard work, than 3 different and incompatible libraries..
The beauty of opensourcing is that everything is possible, isn't it ? So hey, what about sitting around a table and talking ?
Related :
Cheers :)
Hi @cyrilchapon, thanks for sharing your thoughts. I definitely understand that it can be difficult to research and choose among the multiple options for a React component for driving a Mapbox GL JS map. I'd love to discuss what we might be able to do to better help people who want to use Mapbox GL JS in a React application and don't know where to start / how best to invest their energy.
Some initial thoughts:
This blog post, and the associated examples repo, attempts to provide a bit of guidance about using Mapbox GL JS with React. Is this helpful? Is there something we could do to make it more helpful?
As the blog post above mentions, and as @ibgreen articulates, I think it's doubtful that a single "official" <Mapbox /> component could adequately satisfy the wide variety of use cases that Mapbox GL JS intends to serve. It's not just that people need different aspects of the GL JS API to be exposed through the component. It's that there can be differences--pretty deep ones--in how they need GL JS to behave.
For example: should the component behave like a "controlled" or an "uncontrolled" input? (This is a significant issue with no right answer, because Map objects track quite a bit of complex state. Going "controlled" means that the developer now has to track all of that state instead, which is a large burden; going "uncontrolled" may mean dealing with multiple "sources of truth" in your app's state.) Should camera animations be exposed? How so? How about style property transitions? And queryRenderedFeatures?
Ultimately, my personal take is that having a diverse set of React components for Mapbox GL JS, each tailored to slightly (or vastly) different use cases, is a net positive, even though, again, I acknowledge that it comes with very real challenges for developers.
@anandthakker applying the same logic, one might say that having a diverse set of Mapbox GL JS libraries each tailored to slightly (or vastly) different use cases...
I think with a proper design the React component can satisfy most of the consumers, just like the Mapbox GL JS does. The current diversity of the components might be caused by the lack of understanding of all tricky details of the library and because external developers don't know/don't keep the components in sync with new features added to the Mapbox GL JS.
I believe the Mapbox engineers are the best candidates for implementing a React component that plays nicely with the Mapbox GL JS (with the motivation to increase the usage of the library), but I understand that there just might be not enough of resources on your side.
The question of "controlled vs uncontrolled" component should be possible to solve by having a stateless component and a stateful wrapper around it, so that users can choose according to their needs.
@NameFILIP good point -- maybe I'm being too pessimistic. As my colleague @lucaswoj reminded me, we do have a React Native library where many of the design challenges I alluded to above have already been tackled handily. That's a strong indication that the same could be done for React.js. (It also suggests that this would, indeed, require significant dedicated resources, since it would amount to a pretty substantial library in its own right.)
I'd love to hear more from advocates for a React library about the benefits it would provide — benefits to justify the maintenance burden.
The basic React example that @anandthakker mentioned above shows how you can use React's built-in functionality to hook up a GL JS map to a React component. A basic <Map /> component could have no more code than that; could expose the GL JS map instance via React's ref API; and then the parent component could do whatever it wants by using GL JS directly. Such a component probably wouldn't be worth packaging up and open sourcing, because it doesn't contain much of anything.
A React library could add some layers to expose the GL JS API in different ways (with component abstractions, props instead of function arguments, etc.), instead of asking the user to hook up to the map instance directly. But with those additional layers would come a lot of additional maintenance burden. Documentation would need to duplicated; new features would need to be added in both places; many more tests would have to be written; more maintainers would need to be paying attention to more things; etc.
I'm worried that a library would end up doing little more than allowing React users to write code with components instead of functions, props instead of arguments. And I guess I don't think that would be worth its weight.
So: I think it would be super important to list out specific benefits that a React library would aim to provide (beyond the function-component, argument-prop, json-xml switches). Put as a question: What features would make a React library better than the ref-using example?
Hey :) It pleasant to see that much interest for this post :)
What features would make a React library better than the ref-using example ?
[...] beyond the function-component, argument-prop, json-xml switches
Well I may not be enough experienced in React, but the main purposes I could extract from the philosophy of it, are :
Expanding on it, every DOM-based stuff (beyond the js lib itself), where React can shine
Event handling :
@alex3165 in his lib, propose a bottom-up but still declarative approach to creating sources and layers. It allows to have
<Layer //styleProps, like type='line' />
<Feature coordinates=[2, 50] />
</Layer>
And this whole concept allows to define event handling at feature level and makes it a breeze, especially for dragging
Just wanted to hop into the discussion as I'm using react-mapbox-gl module right now in a project. With the way my map works I decided to use a custom class to handle the addition of sources, layers, and popups using mapbox-gl module instead. Open to comments.
@navenedrob can you paste something ?
Sure, it's pretty simple.
This is my Map component. As you can see I'm just leveraging react-mapbox-gl to render the component (using settings exported from my config file). Then I am using the styleLoad property to execute my custom method and instantiate my custom class. Once I do that, I pass the instance to a custom method (a prop passed to my Map component) in the parent, which updates its state. Each component (page, or state-driven content) gets passed the instance as a prop so I have access to everything I need all the time.
Since my map needed to be controlled so granularly I elected to NOT use the other components provided by react-mapbox-gl. I felt that leveraging my own custom class and mapbox-gl instead allowed me more flexibility for my use case. If you have a more static map, that doesn't need to be changed between pages or when state-driven content is updated then I would use the components. I'm adding and removing layers and sources a lot so I didn't want to have to keep up with ref's to the layer components and so on.
import React, { Component } from 'react';
import ReactMapboxGl from 'react-mapbox-gl';
import Mapbox from '../vendor/mapbox/Mapbox';
import { config } from '../vendor/mapbox/settings';
export default class extends Component {
constructor() {
super();
const Component = ReactMapboxGl(config.factory);
this.Map = <Component {...config.component} onStyleLoad={Map => this._styleLoad(Map)} />;
}
// https://reactjs.org/docs/react-component.html#shouldcomponentupdate
shouldComponentUpdate() {
return false;
}
_styleLoad(Map) {
const instance = new Mapbox(Map, this.props.workspace);
const workspace = instance.Api.Utility.getWorkspace(Map.getContainer(), this.props.workspace);
workspace.classList.add('fadeIn');
this.props.updateMapState(instance);
}
render() {
return(this.Map);
}
}
@navenedrob Just a side comment: you should not instantiate the map factory in the render function of your component, that's probably why you get the map flashing and need this shouldComponentUpdate logic to prevent that.
@alex3165 I don't get any flashing, shouldComponentUpdate is there because the Map component is rendered in a parent that does update its state. I don't need to have it rendered every time the state changes so I disable it this way.
I moved it to the constructor. Love your work so far, thank you.
now has to track all of that state instead
Draft.js has solved this by exposing special class that handles all modifications of the state. Also it provides a very nice way to modify state like moving a caret, pasting text, etc. Please, take a look of their design, this is a very easy to use. The only problem is that state is immutable that can be janky for maps (because of GC).
Right now Uber's map is slower than plain MapBox integration since it uses "jumpTo" method that seems to bee a little bit slow than the code that is used internally for zooming.
Hi Guys.
I'm currently the one who wrote the code on the AlpacaTravel implementation. Happy to be available discussion and share some of the design that I was hoping to achieve.
I apologise for writing 'yet another' implementation of the code base which maybe people are upset about. Considering the extent and preference/ideals of the way people like to manage concepts of camera state, styles, interaction, events etc between react state management and mapbox, I think what we are simply encountering different expressions of solution design. I am not sure that there is a one library fits all model.
I ideally aimed to have read only API exposed back to react through implementing a map events component. By making it read only, you could expose the concept of accessing state based API, without the application modifying it and potentially not lining up with the current 'props'. The component could act entirely as controlled via props, and therefore controlled through react state management.
This is a decision to be made through implementation via the author. I use a lot of programmatic style management, including compositing source and layers in redux state, and it didn't suit for me to implement layers and map features through semantic child components (especially as it would be translating the already structured style back out to components). I also did not want to re-implement interactions with the map and animate those props to recreate motion (which is already a key optimised feature of mapbox itself).
What I was aiming to do with this was:
create a slim implementation
expose as much of the API/events of mapbox but 'read only' to discourage state mutation or conflict with props
allow different parts of your application/components to observe state change events easily
not commit to offer semantic style/feature components or interactive/motion helpers
I feel that the other libraries do a good job if you believe that the entire interactions should be controlled through react or if you want to semantically component'ise the layers/features. Props to those guys. But for my/our goals, it was to remain as close to using the API in a react world, without being opinionated about state/components. These other libraries though have faster learning, as I feel that my implementation was when I was way more familiar with the actual mapbox API and wanted to use it as close as I could.
Considering the large API, and potential flexibility that a user may seek, or the ideals they have, or preference to manage things like interaction, differing state or style management, easy on-ramping, advanced behaviours, documentation and examples, matching components with component releases, etc, I am again not sure whether one library could easily do this. Well, maybe, it's all possible, just who has the resources to support such a large undertaking and unify opinions?
We developed with the react-native implementation before the latest major revision, so I am keen to see how some of these discussions were tackled.
Some final opinions, I think the Uber implementation focuses to support other platform implementations that sit on top of it, such as the deck.gl package. That stuff looks awesome. I used this library a lot when I started, hence why the implementation exposes additional uber style props. I didn't use the other library as I felt more comfortable using the mapbox style objects. After spending virtually full-time on our mapbox project for the last 2 years I wanted to build something that I could access the API, without building a rod for my back (such as simply modifying the map instance directly).
Thanks guys, mapbox you guys rule, keep up the good work,
Cheers
Cam
There is also a new one: https://github.com/urbica/react-map-gl by @stepankuzmin.
Not sure what the differences are from the others listed above.
Alex's Mapbox wrapper (which is probably the highest quality of the bunch) is now basically broken as of his latest major version. Perhaps now that Mapbox is considered "a FAANG level effort" and changed their license to make all usage billable you could reconsider official React and Vue implementations.
Most, if not all, "big tech" companies and smaller startups are using React/Preact or Vue to build their products. I imagine that very few people are in situations where the vanilla JS library alone will provide what they need.
As far as controlled vs uncontrolled behavior (an objection brought up when discussing the potential routes of implementing), why not both? I have seen plenty of components that are capable of operating independently but become controlled once you provide for example a viewport prop.
@agusterodin just started porting some old maps to alex's mapbox wrapper and worked great until I started working geojson hover styles that are broken, but fixed with a PR that's been sitting for some time and several folks have inquired about merging. any idea why project management seems stalled over there?
This project is practically abandoned. Kind of the epitome of this super fragmented mess of a map scene for web developers.
The problem is that nobody sponsors or donates to these projects so the developers don't bother since they have lives and can't make a living off of giving people software for free.
Most helpful comment
I'd love to hear more from advocates for a React library about the benefits it would provide — benefits to justify the maintenance burden.
The basic React example that @anandthakker mentioned above shows how you can use React's built-in functionality to hook up a GL JS map to a React component. A basic
<Map />component could have no more code than that; could expose the GL JSmapinstance via React'srefAPI; and then the parent component could do whatever it wants by using GL JS directly. Such a component probably wouldn't be worth packaging up and open sourcing, because it doesn't contain much of anything.A React library could add some layers to expose the GL JS API in different ways (with component abstractions, props instead of function arguments, etc.), instead of asking the user to hook up to the
mapinstance directly. But with those additional layers would come a lot of additional maintenance burden. Documentation would need to duplicated; new features would need to be added in both places; many more tests would have to be written; more maintainers would need to be paying attention to more things; etc.I'm worried that a library would end up doing little more than allowing React users to write code with components instead of functions, props instead of arguments. And I guess I don't think that would be worth its weight.
So: I think it would be super important to list out specific benefits that a React library would aim to provide (beyond the function-component, argument-prop, json-xml switches). Put as a question: What features would make a React library better than the
ref-using example?