|
| 1 | +# Vue createContext |
| 2 | + |
| 3 | +[](https://badge.fury.io/js/vue-create-context) |
| 4 | + |
| 5 | +An abstracted util factory for creating declarative and reactive contexts component in Vue. This API abstraction is greatly inspired by [`React.createContexts`](https://reactjs.org/docs/context.html#reactcreatecontext). The usage and its behaviour is exactly the same as you may expect if you already familiar with React. Under the hood, it uses Vue's reactive system with its `provide/inject` and scoped-slot API. It is light weight, declarative, and easy to use. |
| 6 | + |
| 7 | +## 🧰 Requirements |
| 8 | + |
| 9 | +This library requires Vue 2.6+, where Vue introduces the `v-slot` derivative, opening a new declarative pattern for passing component props in a compositional manner. |
| 10 | + |
| 11 | +## 🎬 Getting started |
| 12 | + |
| 13 | +To get it started, add this package into your project: |
| 14 | + |
| 15 | +```bash |
| 16 | +yarn add -D vue-create-context |
| 17 | +``` |
| 18 | + |
| 19 | +## 📔 API |
| 20 | + |
| 21 | +### `createContext` |
| 22 | + |
| 23 | +```js |
| 24 | +import { createContext } from 'vue-create-context'; |
| 25 | +const MyContext = createContext(defaultValue); |
| 26 | +``` |
| 27 | + |
| 28 | +Calling `createContext` with the `defaultValue` to create a context object. The `defaultValue` can be either a reference object or primitive, which is **ONLY** used when the `Consumer` component can not find its paired `Provider` above the rendering tree. The behaviour is the same as the one in React, so you can also have a look at [React.createContext](https://reactjs.org/docs/context.html#reactcreatecontext). |
| 29 | + |
| 30 | +### `Context.Provider` |
| 31 | + |
| 32 | +```vue |
| 33 | +<MyContext.Provider :value={/* the provided value */}> |
| 34 | +``` |
| 35 | + |
| 36 | +The `Provider` accept a `value` prop to be any value you want it to pass down the rendering tree. Similar to React, you can use this component multiple times an any level of the rendering tree. Its conjugated `Consumer` will receive the value from the closest `Provider` among its ancestors. |
| 37 | + |
| 38 | +Note: If the provided value is reactive, update this value "reactively" will also update all its subscribed descended `Consumers`. |
| 39 | + |
| 40 | +### `Context.Consumer` |
| 41 | + |
| 42 | +```vue |
| 43 | +<MyContext.Consumer v-slot="/* slot props: the value injected by the closed Provider */"> |
| 44 | + /* you can access the value within the block */ |
| 45 | +</MyContext.Consumer> |
| 46 | +``` |
| 47 | + |
| 48 | +The `Consumer` gives the access to the injected value from the closest `Provider`. Unlike React, where uses the CAAF (children as a function, also known as the "render prop") pattern to access the value, we uses `v-slot` inside the component block template to access the value (the so called "slot props"). If you uses single file component (SFC) or browsers supports ES6+ object spread operator, you can take the advantage of object destructuring (see more on [Vue's official page](https://vuejs.org/v2/guide/components-slots.html#Destructuring-Slot-Props)). |
| 49 | + |
| 50 | +It is worth to mention that due to the current limitation of Vue's scoped slot API, the slot props have to be an object, so it is recommended to give the value as an plan old javascript object (POJO). In the case of the provided value to be a primitive, it will be normalized as an object with a `value` key to get the passed value in `v-slot`, i.e. `{ value: /* your provided value */ }`. |
| 51 | + |
| 52 | +Note. You might be tempted to mutate the injected value from the consumer. This is a bad idea since it violate the "[props down event up principle](https://vuejs.org/v2/style-guide/#Implicit-parent-child-communication-use-with-caution)". Under the hood, the library leverage the `computed` property in the `Consumer`, and you would not able to update/mutate on the source value. This is also consistent with the behaviour of Vue's `provide/inject` API. |
| 53 | + |
| 54 | +## 💎 Example |
| 55 | + |
| 56 | +There is an example in [the Official Storybook Vue](https://storybooks-vue.netlify.com/?path=/story/addon-contexts--languages) as an example of the `createContexts`. |
| 57 | + |
| 58 | +For people using the SFC format, here is an conceptual example: |
| 59 | + |
| 60 | +```vue |
| 61 | +<template> |
| 62 | + <div> |
| 63 | + <Provider :value="currentUser"> |
| 64 | + <Consumer v-slot="{ firstName, lastName }"> |
| 65 | + <p>Hello {{ lastName }}, {{ firstName }}!</p> |
| 66 | + <!-- Hello Newman, Jack! --> |
| 67 | + </Consumer> |
| 68 | + </Provider> |
| 69 | + <Consumer v-slot="{ firstName, lastName }"> |
| 70 | + <p>Hello {{ firstName }}, {{ lastName }}!</p> |
| 71 | + <!-- Hello N/A, N/A! --> |
| 72 | + </Consumer> |
| 73 | + </div> |
| 74 | +</template> |
| 75 | +
|
| 76 | +<script> |
| 77 | +import { createContext } from 'vue-create-context'; |
| 78 | +const userContext = createContext({ firstName: 'N/A', lastName: 'N/A' }); // normally it is imported from other files. |
| 79 | +
|
| 80 | +module.exports = { |
| 81 | + name: 'MyComponent', |
| 82 | + components: { |
| 83 | + Provider: userContext.Provider, |
| 84 | + Consumer: userContext.Consumer, |
| 85 | + }, |
| 86 | + data() { |
| 87 | + return { |
| 88 | + firstName: 'Jack', |
| 89 | + lastName: 'Newman', |
| 90 | + }; |
| 91 | + }, |
| 92 | +}; |
| 93 | +</script> |
| 94 | +``` |
| 95 | + |
| 96 | +## 📖 License |
| 97 | + |
| 98 | +MIT |
0 commit comments