diff --git a/app/docs/md/conventions/browser.md b/app/docs/md/conventions/browser.md index af64617d..e4414a1a 100644 --- a/app/docs/md/conventions/browser.md +++ b/app/docs/md/conventions/browser.md @@ -77,7 +77,7 @@ Write a pure function for returning the HTML markup for the `my-message.mjs` cus ```javascript export default function MyMessage({ html, state }) { - const { attrs=[] } = state + const { attrs={} } = state const { message='' } = attrs return html`

${ message }

` @@ -117,12 +117,7 @@ class MyMessageElement extends HTMLElement { } html(strings, ...values) { - const collect = [] - for (let i = 0; i < strings.length - 1; i++) { - collect.push(strings[i], values[i]) - } - collect.push(strings[strings.length - 1]) - return collect.join('') + return String.raw({ raw: strings }, ...values) } static get observedAttributes() { diff --git a/app/docs/md/elements/state/context.md b/app/docs/md/elements/state/context.md new file mode 100644 index 00000000..ed1d7150 --- /dev/null +++ b/app/docs/md/elements/state/context.md @@ -0,0 +1,77 @@ +--- +title: Context +--- + +The context object enables passing state to child elements without needing to resort to passing attributes down through multiple elements. + +## Set parent context + +The context object is passed as a key on the state object. Add data to the context object and it will be available to child elements. + +[Follow along by checking out the context demo from GitHub →](https://github.com/enhance-dev/context-demo) + +Given this markup you can use the context object to pass state directly to a deeply nested child element. Consider the page structure in the example below: + + + +```html + + + + + + + + + +``` + + + + +Add a heading key to the context object in the parent element: + + + +```javascript +export default function ContextParent({ html, state }) { + const { context } = state + context.heading = 'Heading set via context' + return html` + + + ` +} +``` + + + +Render the heading passed via context in the deeply nested child element: + + +```javascript +export default function ContextHeading({ html, state }) { + const { context } = state + const { heading='Default heading' } = context + return html` + +

${heading}

+ ` +} +``` + +
+ + diff --git a/app/docs/md/elements/state/index.md b/app/docs/md/elements/state/index.md index dedcd464..2798fca2 100644 --- a/app/docs/md/elements/state/index.md +++ b/app/docs/md/elements/state/index.md @@ -38,12 +38,14 @@ export default function MyElement ({ html, state }) { -The state object contains two top level entries: +The state object contains four top level keys: - `attrs`, which contains all the key value pairs of attributes passed into your custom element’s instance - `store`, which contains the global state of your Enhance application +- `instanceID`, which is a unique ID per instance of Custom Element +- `context`, which is an Object that can be used to pass state to child elements to avoid prop drilling -These two different entries allow you to work with both basic and complex state in powerful ways without the need for complex abstractions or third party libraries. +These keys allow you to work with both basic and complex state in powerful ways without the need for complex abstractions or third party libraries. diff --git a/app/docs/md/elements/state/instance-id.md b/app/docs/md/elements/state/instance-id.md new file mode 100644 index 00000000..e7732fff --- /dev/null +++ b/app/docs/md/elements/state/instance-id.md @@ -0,0 +1,23 @@ +--- +title: Instance ID +--- + +`instanceID` is a unique identifier that is generated per Custom Element instance. This enables you to differentiate between multiple instances of the same element on a page. It can also be useful when using a string based diffing library like [Morphdom](https://github.com/patrick-steele-idem/morphdom) which keys on unique identifiers to know when to update versus replace an element. + +```javascript +export default function MyCard({ html, state }) { + const { attrs={}, instanceID='' } = state + const { content='', heading='' } = attrs + + return html` +
+

${heading}

+

${content}

+
+ ` +} +``` + + + Pro tip: As in the example above, prefix the id with the element name in order to use the id with multiple child elements. + diff --git a/app/docs/nav-data.mjs b/app/docs/nav-data.mjs index f1f4adf1..0524d959 100644 --- a/app/docs/nav-data.mjs +++ b/app/docs/nav-data.mjs @@ -82,7 +82,15 @@ export const data = [ path: '/docs/elements/state/', label: 'State', hasChildren: true, - items: ['attributes', 'store'], + items: [ + 'attributes', + 'store', + { + slug: 'instance-id', + label: 'Instance ID', + }, + 'context', + ], }, ], }, diff --git a/app/elements/docs/theme-toggle.mjs b/app/elements/docs/theme-toggle.mjs index ecba35d8..99bea43e 100644 --- a/app/elements/docs/theme-toggle.mjs +++ b/app/elements/docs/theme-toggle.mjs @@ -43,8 +43,7 @@ export default function DocsThemeToggle({ html }) {