From what I understand, all of our React app’s components are wrapped with the same shared context ChannelProvider
. They all stem out of <div id="root></div>
… except for a component called Sidebar
. Sidebar
is also wrapped with ChannelProvider
, but it seems like it utilises a separate context.. due to being (I assume) part of a different React DOM. You’ll see the reason why for this in LibraryService
‘s code.
It’s important for me to be able to access the same channelId
state, as defined in my ChannelProvider
, in both Map
and Sidebar
. Is there a way that I can somehow achieve this? I was looking into Portals but admittedly I’m still very unsure.
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>My App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
index.tsx
:
import ReactDOM from 'react-dom/client';
import App from './App';
import reportWebVitals from './reportWebVitals';
console.log('index initialise app');
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<App />
);
reportWebVitals();
channel.context.tsx
:
import React, { createContext, useState, useContext, ReactNode } from 'react';
interface ChannelProviderProps {
children: ReactNode;
}
const ChannelContext = createContext<{ channelId: string; setChannelId: React.Dispatch<React.SetStateAction<string>> } | null>(null);
export const ChannelProvider = ({ children }: ChannelProviderProps) => {
const [channelId, setChannelId] = useState<string>('Initial');
return <ChannelContext.Provider value={{ channelId, setChannelId }}>{children}</ChannelContext.Provider>;
};
export function useChannel() {
const context = useContext(ChannelContext);
if (!context) {
const errorMessage = `Existing context not found.n useChannel must be used within a ChannelProvider`;
throw new Error(errorMessage);
}
return context;
};
app.tsx
:
import './App.css';
import { ChannelProvider } from './contexts/channel.context';
import MapRoutes from './routes/MapRoutes';
function App() {
console.log('Dashboard App');
return (
<div className="App">
<ChannelProvider>
<MapRoutes />
</ChannelProvider>
</div>
);
}
export default App;
maproutes.tsx
:
import { Navigate, BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Map from '../components/Map/Map';
import { tConfig, hConfig } from '../config/configuration';
function MapRoutes() {
return (
<Router>
<Routes>
<Route path="/" element={<Navigate to="/t" />} />
<Route path="/t" element={<Map config={tConfig} />} />
<Route path="/h" element={<Map config={hConfig} />} />
</Routes>
</Router>
);
}
export default MapRoutes;
map.tsx
(where sidebar-panel’s div sits):
interface MapProps {
config: Configuration;
}
function Map(props: MapProps) {
return (
<div className="map">
<nav id="menu"></nav>
<div id="sidebar-panel"></div>
<div className="map-stats-bar">
<p>
<b>Longitude</b>
<span>{longitude.toFixed(4)}</span>{' '}
</p>
<p>
<b>Latitude</b> <span>{latitude.toFixed(4)}</span>
</p>
<p>
<b>Zoom</b>
<span>{zoom}</span>{' '}
</p>
</div>
<div id="version-watermark"></div>
<div ref={mapContainer} className="map-container" />
</div>
);
}
export default Map;
LibraryService.tsx
:
export class LibraryService
protected map: React.MutableRefObject<mapboxgl.Map | null> = useRef<mapboxgl.Map | null>(null);
protected ref = useRef<HTMLDivElement | null>(null);
protected popUpPixelOffset: mapboxgl.Offset = -23;
protected displaySideBar = true;
...
public displaySideBarPanel(
content: CustomConfig,
addSelectedLayer: boolean,
statusList?: Map<string, Status>
) {
const sidebarPanel = document.getElementById('sidebar-panel');
if (!this.map.current) return;
const selectedLayer: SelectedLayer = {
selectedLayerId: this.sourceId,
selectedImage: this.selectedIcon,
map: this.map.current
};
const map: MutableRefObject<mapboxgl.Map | null> = this.map;
if (sidebarPanel) {
let sidebar;
if (addSelectedLayer) {
sidebar = (
<ChannelProvider>
<SideBar
config={content}
selectedLayer={selectedLayer}
statusList={statusList}
map={map}
isSidebarVisible={true}
showMoreSettings={false}
/>
</ChannelProvider>
);
}
const rootElement = createRoot(sidebarPanel);
rootElement.render(sidebar);
}
}
I understand that createRoot
etc. is a major culprit here. But if some kind of cross-context sharing is possible (are portals relevant or did I waste time even looking into that?) that would be really good.
I’ve tried to make sense of Portals but any example I’ve followed simply hasn’t rendered anything.