Skip to content

Commit

Permalink
Update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinkucharczyk committed Sep 27, 2023
1 parent b2d092c commit 90c8b30
Showing 1 changed file with 63 additions and 65 deletions.
128 changes: 63 additions & 65 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,109 +15,107 @@ ember install @customerio/ember-context
```

## Usage
A new context can be created with the `createContext` function:
### Context providers
Data can be provided to all of a component's descendants in one of two ways:
1. The `provide` decorator
2. The `ContextProvider` component

#### `@provide`
This library exports a decorator, which can be used on any getter or property to
expose it as context to all descendants of the component. The decorator requires
a string argument that is the name of the context, which will be used by
descendants to look up the value.

```ts
import { createContext } from '@customerio/ember-context';
import Component from '@glimmer/component';
import { provide } from '@customerio/ember-context';

export const ThemeContext = createContext<'dark' | 'light'>('light');
export default class MyComponent extends Component {
@provide('my-context-name')
get someState() {
return 'some value';
}
}
```

The function returns an object which contains two properties: `Provider` and
`Consumer`, both of which are Glimmer components.

The `Provider` component allows us to provide a context value to anything that's
nested within it. It accepts a `@value` argument, which will be the context's
value.
It is possible to expose tracked properties in context, by adding a
`@provide`-decorated getter, like so:

```ts
import Component from '@glimmer/component';
import { ThemeContext } from 'my-app/contexts/theme';
import { provide } from '@customerio/ember-context';

export default class MyComponent extends Component {
ThemeContext = ThemeContext;
@tracked myTrackedValue = 'some value';

@tracked theme = 'light';
@provide('my-context-name')
get someState() {
return this.myTrackedValue;
// or expressions like return this.args.someArgument;
}
}
```

```hbs
<this.ThemeContext.Provider @value={{this.theme}}>
... anything inside here, no matter how deeply nested, will have access to the value...
</this.ThemeContext.Provider>
```
Any descendants retrieving this context will automatically recompute when the
tracked property changes.

Note: For the purposes of the decorator, it doesn't matter what the getter is
named. The value of the getter will be exposed under the context name.

The `Consumer` component can then be used to retrieve the value of the context
anywhere within the `Provider`s tree:
```ts
import Component from '@glimmer/component';
import { ThemeContext } from 'my-app/contexts/theme';

export default class MyChildComponent extends Component {
ThemeContext = ThemeContext;
}
```
#### `ContextProvider`
Alternatively, this addon also provides a `ContextProvider` component:

```hbs
<this.ThemeContext.Consumer as |value|>
{{value}}
</this.ThemeContext.Consumer>
<ContextProvider @key="my-context-name" @value={{this.someValue}}>
{{! descendant components can now look up "my-context-name" }}
</ContextProvider>
```

The above would print `light`, which is the current value of the context. If the
context value is a tracked property and it changes, the consumers will update
accordingly.

### `inject`
A context can also be consumed from within a component class, similarly to how
Ember Services are consumed:
### Context consumers
There are also two ways to retrieve a context value:
1. Using the `consume` decorator
2. Using the `ContextConsumer` component

#### `@consume`
The `consume` decorator allows us to retrieve a context value in a way that's
similar to working with an Ember service:

```ts
import Component from '@glimmer/component';
import { ThemeContext } from 'my-app/contexts/theme';
import { inject as context, type contextValueType } from '@customerio/ember-context';
import { consume } from '@customerio/ember-context';

export default class MyChildComponent extends Component {
@context(ThemeContext) theme!: contextValueType<typeof ThemeContext>;
@consume('my-context-name') myContextValue!: string;
}
```

```hbs
<div ...attributes>
{{this.theme}}
</div>
<p>The context value is: {{this.myContextValue}}</p>
```

This would also print `light`, and would update when the context value changes.
#### `ContextConsumer`
Alternatively, a context value can be retrieved with the `ContextConsumer`
component. It accepts a `@key` string argument, which is the name of the
context, and it yields the value of the context:
```hbs
<ContextConsumer @key="my-context-name" as |value|>
{{value}}
</ContextConsumer>
```

With this decorator, context values can also be used in getters:
### TypeScript
Currently, context names are just strings, and this library doesn't yet provide
a way to automatically assign or infer types based on context names.

```ts
import Component from '@glimmer/component';
import { ThemeContext } from 'my-app/contexts/theme';
import { inject as context, type contextValueType } from '@customerio/ember-context';

export default class MyChildComponent extends Component {
@context(ThemeContext) theme!: contextValueType<typeof ThemeContext>;

get classNames() {
if (this.theme === 'light') {
return 'theme-light';
} else if (this.theme === 'dark') {
return 'theme-dark';
}
}
}
```

## Inspiration
The idea was to create an API similar to the Context API in React
- [`React Context API`](https://react.dev/reference/react/createContext): The
addon aims to implement a context API for Ember that resembles how React
handles contexts (where React's `useContext` hook is replaced by the `inject`
decorator, which is more Ember idiomatic)
original inspiration for this project is, of course, the React Context
- [`Svelte Context API`](https://svelte.dev/docs/svelte#setcontext): Svelte
exposes contexts via simple `setContext` and `getContext` functions
- [`Vue provide/inject`](https://vuejs.org/guide/components/provide-inject.html#provide):
Vue allows working with context via `provide` and `inject` functions
- [`ember-context`](https://github.com/alexlafroscia/ember-context): Another
Ember addon that also implements a similar Context API. However, that addon's
implementation relies has providers racing on a provider key, while our addon
Expand Down

0 comments on commit 90c8b30

Please sign in to comment.