add section on context

pull/2179/head
Rich Harris 7 years ago
parent a7aaf6e841
commit ac5181bef8

@ -0,0 +1,13 @@
<script>
import Map from './Map.svelte';
import MapMarker from './MapMarker.svelte';
</script>
<Map lat={35} lon={-84} zoom={3.5}>
<MapMarker lat={37.8225} lon={-122.0024} label="Svelte Body Shaping"/>
<MapMarker lat={33.8981} lon={-118.4169} label="Svelte Barbershop & Essentials"/>
<MapMarker lat={29.7230} lon={-95.4189} label="Svelte Waxing Studio"/>
<MapMarker lat={28.3378} lon={-81.3966} label="Svelte 30 Nutritional Consultants"/>
<MapMarker lat={40.6483} lon={-74.0237} label="Svelte Brands LLC"/>
<MapMarker lat={40.6986} lon={-74.4100} label="Svelte Medical Systems"/>
</Map>

@ -0,0 +1,48 @@
<script>
import { onMount } from 'svelte';
import { mapbox } from './mapbox.js';
// set the context here...
export let lat;
export let lon;
export let zoom;
let container;
let map;
onMount(() => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://unpkg.com/mapbox-gl/dist/mapbox-gl.css';
link.onload = () => {
map = new mapbox.Map({
container,
style: 'mapbox://styles/mapbox/streets-v9',
center: [lon, lat],
zoom
});
};
document.head.appendChild(link);
return () => {
map.remove();
link.parentNode.removeChild(link);
};
});
</script>
<style>
div {
width: 100%;
height: 100%;
}
</style>
<div bind:this={container}>
{#if map}
<slot></slot>
{/if}
</div>

@ -0,0 +1,17 @@
<script>
import { mapbox } from './mapbox.js';
// ...get the context here
export let lat;
export let lon;
export let label;
const popup = new mapbox.Popup({ offset: 25 })
.setText(label);
const marker = new mapbox.Marker()
.setLngLat([lon, lat])
.setPopup(popup)
.addTo(map);
</script>

@ -0,0 +1,8 @@
import mapbox from 'mapbox-gl';
// https://docs.mapbox.com/help/glossary/access-token/
mapbox.accessToken = MAPBOX_ACCESS_TOKEN;
const key = {};
export { mapbox, key };

@ -0,0 +1,13 @@
<script>
import Map from './Map.svelte';
import MapMarker from './MapMarker.svelte';
</script>
<Map lat={35} lon={-84} zoom={3.5}>
<MapMarker lat={37.8225} lon={-122.0024} label="Svelte Body Shaping"/>
<MapMarker lat={33.8981} lon={-118.4169} label="Svelte Barbershop & Essentials"/>
<MapMarker lat={29.7230} lon={-95.4189} label="Svelte Waxing Studio"/>
<MapMarker lat={28.3378} lon={-81.3966} label="Svelte 30 Nutritional Consultants"/>
<MapMarker lat={40.6483} lon={-74.0237} label="Svelte Brands LLC"/>
<MapMarker lat={40.6986} lon={-74.4100} label="Svelte Medical Systems"/>
</Map>

@ -0,0 +1,50 @@
<script>
import { onMount, setContext } from 'svelte';
import { mapbox, key } from './mapbox.js';
setContext(key, {
getMap: () => map
});
export let lat;
export let lon;
export let zoom;
let container;
let map;
onMount(() => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'https://unpkg.com/mapbox-gl/dist/mapbox-gl.css';
link.onload = () => {
map = new mapbox.Map({
container,
style: 'mapbox://styles/mapbox/streets-v9',
center: [lon, lat],
zoom
});
};
document.head.appendChild(link);
return () => {
map.remove();
link.parentNode.removeChild(link);
};
});
</script>
<style>
div {
width: 100%;
height: 100%;
}
</style>
<div bind:this={container}>
{#if map}
<slot></slot>
{/if}
</div>

@ -0,0 +1,19 @@
<script>
import { getContext } from 'svelte';
import { mapbox, key } from './mapbox.js';
const { getMap } = getContext(key);
const map = getMap();
export let lat;
export let lon;
export let label;
const popup = new mapbox.Popup({ offset: 25 })
.setText(label);
const marker = new mapbox.Marker()
.setLngLat([lon, lat])
.setPopup(popup)
.addTo(map);
</script>

@ -0,0 +1,8 @@
import mapbox from 'mapbox-gl';
// https://docs.mapbox.com/help/glossary/access-token/
mapbox.accessToken = MAPBOX_ACCESS_TOKEN;
const key = {};
export { mapbox, key };

@ -0,0 +1,47 @@
---
title: setContext and getContext
---
The context API provides a mechanism for components to 'talk' to each other without passing around data and functions as props, or dispatching lots of events. It's an advanced feature, but a useful one.
Take this example app using a [Mapbox GL](https://docs.mapbox.com/mapbox-gl-js/overview/) map. We'd like to display the markers, using the `<MapMarker>` component, but we don't want to have to pass around a reference to the underlying Mapbox instance as a prop on each component.
There are two halves to the context API — `setContext` and `getContext`. If a component calls `setContext(key, context)`, then any *child* component can retrieve the context with `const context = getContext(key)`.
Let's set the context first. In `Map.svelte`, import `setContext` from `svelte` and `key` from `mapbox.js` and call `setContext`:
```js
import { onMount, setContext } from 'svelte';
import { mapbox, key } from './mapbox.js';
setContext(key, {
getMap: () => map
});
```
The context object can be anything you like. Like [lifecycle functions](tutorial/onmount), `setContext` and `getContext` must be called during component initialisation; since `map` isn't created until the component has mounted, our context object contains a `getMap` function rather than `map` itself.
On the other side of the equation, in `MapMarker.svelte`, we can now get a reference to the Mapbox instance:
```js
import { getContext } from 'svelte';
import { mapbox, key } from './mapbox.js';
const { getMap } = getContext(key);
const map = getMap();
```
The markers can now add themselves to the map.
> A more finished version of `<MapMarker>` would also handle removal and prop changes, but we're only demonstrating context here.
## Context keys
In `mapbox.js` you'll see this line:
```js
const key = {};
```
We can use anything as a key — we could do `setContext('mapbox', ...)` for example. The downside of using a string is that different component libraries might accidentally use the same one; using an object literal means the keys are guaranteed not to conflict in any circumstance, even when you have multiple different contexts operating across many component layers.

@ -0,0 +1,3 @@
{
"title": "Context API"
}

@ -113,6 +113,7 @@ Maybe lifecycle should go first, since we're using `onMount` in the `this` demo?
* [x] Custom JS transitions
* [x] Thunk(?) transitions
* [x] `on:introstart` etc
* [ ] Local transitions
## Animations

@ -6,7 +6,8 @@
"NODE_ENV": "production",
"BASEURL": "https://v3.svelte.technology",
"GITHUB_CLIENT_ID": "@svelte-gist-client-id-v3",
"GITHUB_CLIENT_SECRET": "@svelte-gist-client-secret-v3"
"GITHUB_CLIENT_SECRET": "@svelte-gist-client-secret-v3",
"MAPBOX_ACCESS_TOKEN": "@svelte-mapbox-access-token"
},
"files": [
"__sapper__/build",

@ -1,3 +1,4 @@
import 'dotenv/config';
import resolve from 'rollup-plugin-node-resolve';
import replace from 'rollup-plugin-replace';
import commonjs from 'rollup-plugin-commonjs';
@ -19,7 +20,8 @@ export default {
plugins: [
replace({
'process.browser': true,
'process.env.NODE_ENV': JSON.stringify(mode)
'process.env.NODE_ENV': JSON.stringify(mode),
'process.env.MAPBOX_ACCESS_TOKEN': JSON.stringify(process.env.MAPBOX_ACCESS_TOKEN)
}),
svelte({
dev,

@ -63,9 +63,14 @@
const styles = document.querySelectorAll('style.svelte');
let i = styles.length;
while (i--) styles[i].parentNode.removeChild(styles[i]);
`)
`);
await proxy.eval(`
// needed for context API tutorial
window.MAPBOX_ACCESS_TOKEN = process.env.MAPBOX_ACCESS_TOKEN;
${$bundle.dom.code}
await proxy.eval(`${$bundle.dom.code}
if (window.component) {
try {
window.component.$destroy();

Loading…
Cancel
Save