Skip to content

Commit

Permalink
docs: add readme
Browse files Browse the repository at this point in the history
  • Loading branch information
posva committed Nov 26, 2019
1 parent 567522c commit 2dcc3ef
Showing 1 changed file with 220 additions and 3 deletions.
223 changes: 220 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,235 @@
# pinia [![Build Status](https://badgen.net/circleci/github/posva/pinia/master)](https://circleci.com/gh/posva/pinia) [![npm package](https://badgen.net/npm/v/pinia)](https://www.npmjs.com/package/pinia) [![coverage](https://badgen.net/codecov/c/github/posva/pinia/master)](https://codecov.io/github/posva/pinia) [![thanks](https://badgen.net/badge/thanks/♥/pink)](https://github.com/posva/thanks)

> Some awesome description
> 🍍Type Safe Modular and lightweight (but **Experimental**) Store for Vue based on the composition api
Demo (TODO link)

⚠⚠⚠ This project is experimental, it's an exploration of whan a _Store_ could be like using [the composition api](https://vue-composition-api-rfc.netlify.com). It works for Vue 2 by using the [official library](https://github.com/vuejs/composition-api).

What I want is to maybe inspire others to think about ways to improve Vuex.

There are the core principles that I try to achieve with this experiment:

- Flat modular structure 🍍 No nesting, only stores, compose them as needed
- Light layer on top of Vue 💨 keep it under 1kg gzip
- Only `state` and `getters` 👐 `patch` is the new _mutation_
- Actions are just functions ⚗️ Group your business there
- import what you need, let webpack code split 📦 And you won't need modules!
- SSR support ⚙️
- DevTools support 💻 Which is crucial to make this enjoyable

## FAQ

A few notes about the project and possible questions:

**Q**: _Does this replace Vuex, is it its successor?_
**A**: No, or at least that's not the main intention

**Q**: _What about dynamic modules?_
**A**: Dynamic modules are not type safe, so instead [we allow creating different stores](#TODO) that can be imported anywhere

## Roadmap / Ideas

- [ ] List Getters on DevTools
- [ ] Nuxt Module
- [ ] Automatic fresh store on Server Side?
- [ ] Flag to remove devtools support (for very light production apps)
- [ ] Allow grouping stores together into a similar structure and allow defining new getters

## Installation

```sh
npm install lib-boilerplate
yarn add pinia
# or with npm
npm install pinia
```

## Usage

## API
### Creating a Store

You can create as many stores as you want, and they should each exist in isolated files:

```ts
import { createStore } from 'pinia'

export const useMainStore = createStore(
// name of the store
// it is used in devtools and allows restoring state
'main',
// a function that returns a fresh state
() => ({
counter: 0,
name: 'Eduardo',
}),
// optional getters
{
doubleCount: state => state.counter * 2,
}
)
```

`createStore` returns a function that has to be called to get access to the store:

```ts
import { useMainStore } from '@/stores/main'

export default createComponent({
setup() {
const main = useMainStore()

return {
// gives access to the whole store
main,
// gives access to the state
state: main.state,
// gives access to specific getter,
}
},
})
```

**There is one important rule for this to work**: the `useMainStore` (or any other _useStore_ function) must be called inside of deffered functions. This is to allow the Vue Composition API plugin to be installed. \*\*Never, ever call `useStore` like this:

```ts
import { useMainStore } from '@/stores/main'
// ❌ Depending on where you do this it will fail
// so just don't do it
const main = useMainStore()

export default createComponent({
setup() {
return {}
},
})
```

Once you have access to the store, you can access the `state` through `store.state` and any getter directly on the `store` itself as a _computed_ property (from `@vue/composition-api`) (meaning you need to use `.value` to read the actual value on the JavaScript but not in the template):

```ts
export default createComponent({
setup() {
const main = useMainStore()
const text = main.state.name
const doubleCount = main.doubleCount.value // notice the `.value` at the end
return {}
},
})
```

`state` is the result of a `ref` while every getter is the result of a `computed`. Both from `@vue/composition-api`.

### Mutating the `state`

To mutate the state you can either directly change something:

```ts
main.state.counter++
```

or call the method `patch` that allows you apply multiple changes at the same time with a partial `state` object:

```ts
main.patch({
counter; -1,
name: 'Abalam',
})
```

The main difference here is that `patch` allows you to group multiple changes into one single entry in the devtools.

### Replacing the `state`

Simply set it to a new object;

```ts
main.state = { counter: 666, name: 'Paimon' }
```

### SSR

The main part about SSR is **not sharing `state`** between requests. So we can pass `true` to `useStore` **once** when getting a new request on the server. If we follow [the SSR guide](https://ssr.vuejs.org/guide/data.html), our `createApp` should look like this:

```ts
export function createApp() {
// Here there could also be a router
const store = useStore(true)

// we can change the state now!
store.state.counter++

// create the app instance
const app = new Vue({
render: h => h(App),
})

// expose the app and the store.
return { app, store }
}
```

### Actions

Actions are simply function that contain business logic. As with components, they **must call `useStore`** to retrieve the store:

```ts
export async function login(user, password) {
const store = useUserStore()
const userData = await apiLogin(user, password)

store.patch({
name: user,
...userData,
})
}
```

### Composing Stores

Composing stores may look hard at first glance but there is only one rule to follow really:

If **multiple stores use each other** or you need to use **multiple stores** at the same time, you must create a **separate file** where you import all of them.

If one store uses an other store, there is no need to create a new file, you can directly import it. Think of it as nesting.

#### Shared Getters

If you need to compute a value based on the `state` and/or `getters` of multiple stores, you may be able to import all the stores but one into the remaining store, but depending on how your stores are used across your application, **this would hurt your code splitting** as you importing the store that imports all others stores, would result in **one single big chunk** with all of your stores.
To prevent this, **we follow the rule above** and we create a new file:

```ts
import { computed } from '@vue/composition-api'
import { useUserStore } from './user'
import { useCartStore } from './cart'

export const summary = computed(() => {
const user = useUserStore()
const cart = useCartStore()

return `Hi ${user.state.name}, you have ${cart.state.list.length} items in your cart. It costs ${cart.price}.`
})
```

#### Shared Actions

When an actions needs to use multiple stores, we do the same, we create a new file:

```ts
import { useUserStore } from './user'
import { useCartStore, emptyCart } from './cart'

export async function orderCart() {
const user = useUserStore()
const cart = useCartStore()

try {
await apiOrderCart(user.state.token, cart.state.items)
emptyCart()
} catch (err) {
displayError(err)
}
}
```

## Related

Expand Down

0 comments on commit 2dcc3ef

Please sign in to comment.