Hi, I want to combine data from multiple sources and create my own buttons using a react component. Is there an easy way to replace the out of the box popup with a React component?
Thanks, Tyler
Solved! Go to Solution.
It turns out this can be done. Create a little react component like this:
import React from 'react';
class PopUpContent extends React.Component {
render() {
return (
<div>
<p>Put your rad popup content here</p>
</div>
);
}
}
export default PopUpContent;
Then put it in the ESRI popup like this:
let puNode = document.createElement("div");
self.state.mapView.popup.content = puNode
ReactDOM.render(
<PopUp />,
puNode
);
I hope this helps someone.
It turns out this can be done. Create a little react component like this:
import React from 'react';
class PopUpContent extends React.Component {
render() {
return (
<div>
<p>Put your rad popup content here</p>
</div>
);
}
}
export default PopUpContent;
Then put it in the ESRI popup like this:
let puNode = document.createElement("div");
self.state.mapView.popup.content = puNode
ReactDOM.render(
<PopUp />,
puNode
);
I hope this helps someone.
Hey Tyler - I had a similar need and your code above was extremely helpful. Thank you! One follow up question though: were you using Redux? I am new to Redux and am trying to connect my popup content component to my app's store, but I'm confused as to how you share the store across components that don't share the same root.
When I first call ReactDOM.render() I wrap the root of my app with redux-react's <Provider> component and pass in the store, so all of it's child components have access to the store. But because the popup content doesn't fall within the App's hierarchy and is rendered with it's own ReactDOM.render() call, it doesn't know about the store. If you or anyone else has thoughts on how to provide access to a Redux store across multiple independent React component hierarchies, let me know!
Hey Nathan,
Sorry for the late reply. I haven't logged in for a while. I'm glad the code was helpful.
I am not currently using redux but will be integrating it into my project. If you come up with something could you post what you get? If I end up getting the Redux store integrated I'll post it back up.
Thanks, Tyler
Hey Tyler,
I did figure out a solution, though I am not super confident this approach is considered best practice. I simply export the store after it's created in index.js, and import it from index.js into my popup component and pass it to the popup tree's <Provider>.
So index.js looks like this:
...
export const store = configureStore()
const render = () => ReactDOM.render(
<AppContainer>
<Provider store={ store }>
<App history={ history } />
</Provider>
</AppContainer>,
document.getElementById('root')
)
render()
...
and my popup component looks like this:
import React from 'react'
import ReactDOM from 'react-dom'
import StreamPopup from '../../containers/StreamPopup'
import { Provider } from 'react-redux'
import { store } from '../../index'
const buildPopup = () => {
let popupNode = document.createElement('div')
ReactDOM.render(
<Provider store={ store }>
<StreamPopup />
</Provider>,
popupNode
)
return popupNode
}
export default buildPopup
I had trouble finding any recommendations online about the best way to do this, which was a little surprising given the fact that multiple React trees in the same app are quite common and Redux is so widely used. One thing I would be a little wary of with this approach are race-conditions (if two React trees are 'racing' to change the same state props at the same time).
Maybe you'll have better luck than me finding a definitive solution? If you do, let me know!
Cool. I'm sure this will be helpful to me and many others. Thanks for sharing.
Here's a simple code snippet how we can achieve that in React v18:
if (Array.isArray(testLayer.popupTemplate.content)) {
testLayer.popupTemplate.content.unshift(
new CustomContent({
outFields: ['*'],
creator: () => {
const container = document.createElement('div');
ReactDOM.createRoot(container).render(
<SomeReactButton
title={testLayer.title}
onClick={() => setState(true)}
/>
);
return container;
}
})
);
}
Creating the new root of React is just fine as described in the documentation - https://react.dev/reference/react-dom/client/createRoot#createroot
> An app fully built with React will usually only have one createRoot call for its root component. A page that uses “sprinkles” of React for parts of the page may have as many separate roots as needed.
There are some vital disadvantages and limitations that you should be aware of:
- Different React roots cannot share context as they are in some sense "separate apps" (tiny frontends) - that may cause props drilling in more advanced cases;
- Communication between roots has to fall back on global DOM events or explicitly provided callbacks (with setter from useState for instance) - that may impede the clarity of a written code;
- You get less benefit from optimizations such suspense or concurrent mode;