Skip to content

Commit

Permalink
docs: add documentation on migrating from @googlemaps/react-wrapper (#…
Browse files Browse the repository at this point in the history
…612)

Add a small example and documentation on how to migrate code written with the @googlemaps/react-wrapper library to @vis.gl/react-google-maps.
  • Loading branch information
usefulthink authored Nov 22, 2024
1 parent a5b0359 commit a404662
Show file tree
Hide file tree
Showing 8 changed files with 275 additions and 1 deletion.
89 changes: 89 additions & 0 deletions docs/guides/migrating-from-react-wrapper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Migrating from `@googlemaps/react-wrapper`

The [`@googlemaps/react-wrapper`][npm-react-wrapper] library provides a
minimal wrapper for loading the Maps JavaScript API in a React Application.

If you decide to migrate from using `@googlemaps/react-wrapper` to this
library, this can be done seamlessly by using a poyfill for the `<Wrapper>`
component.

Roughly speaking, our [`<APIProvider>`][rgm-api-provider] component has the
same function as the `<Wrapper>` component provided by
`@googlemaps/react-wrapper`, with one major difference: While the `Wrapper`
component will only render its children once the Google Maps JavaScript API
has been loaded, the `APIProvider` will always render the children and use
custom hooks like [`useApiLoadingStatus()`][rgm-use-api-loading-status] to
handle API loading in its components.

The following code shows how the `Wrapper` component can be implemented with
this library in a fully compatible way, allowing you to use it as a
drop-in-replacement for the `@googlemaps/react-wrapper` library.

[A complete example can be found here][rgm-examples-react-wrapper-migration].

```tsx title="wrapper.tsx"
import React, {
FunctionComponent,
PropsWithChildren,
ReactNode,
useEffect
} from 'react';

import {
APILoadingStatus,
APIProvider,
APIProviderProps,
useApiLoadingStatus
} from '@vis.gl/react-google-maps';

const STATUS_MAP = {
[APILoadingStatus.LOADING]: 'LOADING',
[APILoadingStatus.LOADED]: 'SUCCESS',
[APILoadingStatus.FAILED]: 'FAILURE'
} as const;

type WrapperProps = PropsWithChildren<
{
apiKey: string;
callback?: (status: string) => void;
render?: (status: string) => ReactNode;
} & APIProviderProps
>;

export const Wrapper: FunctionComponent<WrapperProps> = ({
apiKey,
children,
render,
callback,
...apiProps
}) => {
return (
<APIProvider apiKey={apiKey} {...apiProps}>
<InnerWrapper render={render}>{children}</InnerWrapper>
</APIProvider>
);
};

const InnerWrapper = ({
callback,
render,
children
}: PropsWithChildren<Omit<WrapperProps, 'apiKey'>>) => {
const status = useApiLoadingStatus();
const mappedStatus = STATUS_MAP[status] ?? 'LOADING';

useEffect(() => {
if (callback) callback(mappedStatus);
}, [callback, mappedStatus]);

if (status === APILoadingStatus.LOADED) return children;
if (render) return render(mappedStatus);

return <></>;
};
```

[npm-react-wrapper]: https://www.npmjs.com/package/@googlemaps/react-wrapper
[rgm-api-provider]: ../api-reference/components/api-provider.md
[rgm-use-api-loading-status]: ../api-reference/hooks/use-api-loading-status.md
[rgm-examples-react-wrapper-migration]: https://github.com/visgl/react-google-maps/tree/main/examples/react-wrapper-migration
3 changes: 2 additions & 1 deletion docs/table-of-contents.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
"guides/interacting-with-google-maps-api",
"guides/deckgl-integration",
"guides/ssr-and-frameworks",
"guides/writing-examples"
"guides/writing-examples",
"guides/migrating-from-react-wrapper"
]
},
{
Expand Down
36 changes: 36 additions & 0 deletions examples/react-wrapper-migration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Migrating from @googlemaps/react-wrapper

This is an example to show how code that was written using
`@googlemaps/react-wrapper` can be ported to this library.

## Google Maps Platform API key

This example does not come with an API key. Running the examples locally requires a valid API key for the Google Maps Platform.
See [the official documentation][get-api-key] on how to create and configure your own key.

The API key has to be provided via an environment variable `GOOGLE_MAPS_API_KEY`. This can be done by creating a
file named `.env` in the example directory with the following content:

```shell title=".env"
GOOGLE_MAPS_API_KEY="<YOUR API KEY HERE>"
```

If you are on the CodeSandbox playground you can also choose to [provide the API key like this](https://codesandbox.io/docs/learn/environment/secrets)

## Development

Go into the example-directory and run

```shell
npm install
```

To start the example with the local library run

```shell
npm run start-local
```

The regular `npm start` task is only used for the standalone versions of the example (CodeSandbox for example)

[get-api-key]: https://developers.google.com/maps/documentation/javascript/get-api-key
31 changes: 31 additions & 0 deletions examples/react-wrapper-migration/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no" />
<title>Example:</title>

<style>
body {
margin: 0;
font-family: sans-serif;
}
#app {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div id="app"></div>
<script type="module">
import '@vis.gl/react-google-maps/examples.css';
import '@vis.gl/react-google-maps/examples.js';
import {renderToDom} from './src/app';

renderToDom(document.querySelector('#app'));
</script>
</body>
</html>
15 changes: 15 additions & 0 deletions examples/react-wrapper-migration/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "module",
"dependencies": {
"@googlemaps/react-wrapper": "^1.1.42",
"@vis.gl/react-google-maps": "latest",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"vite": "^5.0.4"
},
"scripts": {
"start": "vite",
"start-local": "vite --config ../vite.config.local.js",
"build": "vite build"
}
}
31 changes: 31 additions & 0 deletions examples/react-wrapper-migration/src/app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import {createRoot} from 'react-dom/client';

// import {Wrapper} from '@googlemaps/react-wrapper';
import {Wrapper} from './wrapper';

const API_KEY =
// @ts-ignore
globalThis.GOOGLE_MAPS_API_KEY ?? (process.env.GOOGLE_MAPS_API_KEY as string);

const MapsTest = () => {
return <h1>Google Maps JavaScript API v{google.maps.version} Loaded.</h1>;
};

const App = () => (
<Wrapper apiKey={API_KEY} render={status => <h1>Status: {status}</h1>}>
<MapsTest />
</Wrapper>
);

export default App;

export function renderToDom(container: HTMLElement) {
const root = createRoot(container);

root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
}
54 changes: 54 additions & 0 deletions examples/react-wrapper-migration/src/wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import React, {
FunctionComponent,
PropsWithChildren,
ReactNode,
useEffect
} from 'react';
import {APIProvider, useApiLoadingStatus} from '../../../src';
import {APILoadingStatus, APIProviderProps} from '@vis.gl/react-google-maps';

const statusMap = {
[APILoadingStatus.LOADING]: 'LOADING',
[APILoadingStatus.LOADED]: 'SUCCESS',
[APILoadingStatus.FAILED]: 'FAILURE'
} as const;

type WrapperProps = PropsWithChildren<
{
apiKey: string;
callback?: (status: string) => void;
render?: (status: string) => ReactNode;
} & APIProviderProps
>;

export const Wrapper: FunctionComponent<WrapperProps> = ({
apiKey,
children,
render,
callback,
...apiProps
}) => {
return (
<APIProvider apiKey={apiKey} {...apiProps}>
<InnerWrapper render={render}>{children}</InnerWrapper>
</APIProvider>
);
};

const InnerWrapper = ({
callback,
render,
children
}: PropsWithChildren<Omit<WrapperProps, 'apiKey'>>) => {
const status = useApiLoadingStatus();
const mappedStatus = statusMap[status] ?? 'LOADING';

useEffect(() => {
if (callback) callback(mappedStatus);
}, [callback, mappedStatus]);

if (status === APILoadingStatus.LOADED) return children;
if (render) return render(mappedStatus);

return <></>;
};
17 changes: 17 additions & 0 deletions examples/react-wrapper-migration/vite.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import {defineConfig, loadEnv} from 'vite';

export default defineConfig(({mode}) => {
const {GOOGLE_MAPS_API_KEY = ''} = loadEnv(mode, process.cwd(), '');

return {
define: {
'process.env.GOOGLE_MAPS_API_KEY': JSON.stringify(GOOGLE_MAPS_API_KEY)
},
resolve: {
alias: {
'@vis.gl/react-google-maps/examples.js':
'https://visgl.github.io/react-google-maps/scripts/examples.js'
}
}
};
});

0 comments on commit a404662

Please sign in to comment.