Skip to content
This repository has been archived by the owner on May 16, 2021. It is now read-only.

Commit

Permalink
updated readme
Browse files Browse the repository at this point in the history
  • Loading branch information
Goldziher committed May 11, 2020
1 parent 136f271 commit 7b07cff
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 26 deletions.
141 changes: 124 additions & 17 deletions readme.md
Expand Up @@ -18,37 +18,116 @@ Both factory functions expect an object with the keys being the name of the acti

```typescript
actionFactory({
actionName1: [
actionName1: [{
commit?: string
dispatch?: string
execute?: Function
value?: Function | any
],
actionName2: [
commit?: string
dispatch?: string
execute?: Function
value?: Function | any
],
}],
...
}
)

```

Each action option **must include** either a commit, dispatch or execute prop. Commit and dispatch are strings of the type used by vuex, i.e. for a mutation it would be usually `module/MUTATION_KEY`, and for an action it would be `module/actionName`, execute is a function that will will receive the action context and payload as its arguments.
Each action option **must include** either a commit, dispatch or execute prop.

Commit and dispatch are strings of the type used by vuex, i.e. for a mutation it would be usually `module/MUTATION_KEY`, and for an action it would be `module/actionName`, execute is a function that will will receive the action context and payload as its arguments. _note: root is automatically inferred_

Value in turn can take two different forms:

1. value can be a function. In this case, the commit or dispatch in the given option will be passed `value(payload)` as the payload.
2. value can be any other kind of value. In this case it is regarded as a default value, and it will be passed to the commit or dispatch as long as payload is undefined. If payload is provided, it will take precedence over the default value.
2. value can be any other kind of value. _note: if specified value for a given option will take precedence over a payload, even if payload is passed to the action_

| Option | Use | Example |
| -------- | --------------------------------- | -------------------------------------------------------------- |
| commit | the name of a mutation to commit | commit: "SET_USER" |
| dispatch | the name of an action to dispatch | dispatch: "users/getUser" |
| execute | a function to be executed | execute: (context, payload) => { ... } |
| value | a function or default value | value: (payload: number) => payload / 2 **OR** value: 100 etc. |

| Option | Use | Example |
| -------- | --------------------------------- | --------------------------------------------------------------- |
| commit | the name of a mutation to commit | commit: "SET_USER" |
| dispatch | the name of an action to dispatch | dispatch: "users/getUser", note: root is automatically inferred |
| execute | a function to be executed | execute: (context, payload) => { ... } |
| value | a function or default value | value: (payload: number) => payload / 2 or value: 100 |
The use case for using the actionFactory is to reduce boilterpate actions that do not require complex typing of async operations, e.g. UI related actions. For example, given an actions.js file that looks like so:

```javascript

async function getUserById(ctx, id) {
try {
response = await api.get(...)
ctx.commit("SET_CURRENT_USER", response.data)
} catch(error) {
...
}
}

async function updateUser(ctx, payload) {
try {
response = await api.patch(...)
ctx.commit("SET_CURRENT_USER", response.data)
} catch(error) {
...
}
}

function openEditUserDrawer(ctx) {
ctx.commit("SET_DRAWER_PAYLOAD", ctx.state.currentUser)
}

function closeEditUserDrawer(ctx, payload) {
ctx.commit("SET_DRAWER_PAYLOAD", null)
ctx.dispatch("updateUser", payload)
}

export default {
getUserById,
updateUser,
openEditUserDrawer,
closeEditUserDrawer,
}
```

Here the two UI actions - openCreateEditUserDrawer and closeCreateEditUserDrawer neither perform async operations nor require error handling, as such they are primary candidates to be converted to the actionFactory:

```javascript
import {actionFactory} from 'vuex-factories'

async function getUserById(ctx, id) {
try {
response = await api.get(...)
ctx.commit("SET_CURRENT_USER", response.data)
} catch(error) {
...
}
}

async function updateUser(ctx, payload) {
try {
response = await api.patch(...)
ctx.commit("SET_CURRENT_USER", response.data)
} catch(error) {
...
}
}

export default {
getUserById,
updateUser,
...actionFactory({
openEditUserDrawer: [{
commit: "SET_DRAWER_PAYLOAD",
value: ({state}) => state.currentUser
}],
closeEditUserDrawer: [
{
commit: "SET_DRAWER_PAYLOAD",
value: null
},
{
dispatch: "updateUser"
}
],
})
}
```

### mutation factory

Expand All @@ -66,4 +145,32 @@ mutationFactory({

Each mutation option **must include** a key prop, which represents a state key, e.g. "users". It can also include a value prop. The value prop here acts exactly like it does for actionFactory, that is - it can be either a function, in which case it will be called with the payload as its parameter, or it can be a default value.

Please note - if a mutation is called with a payload and the value prop is not a function, the payload will take precedence over the default value.
The use case for the mutation factory follows similar logic - reducing unnecessary boilerplate. For example:

```javascript
function SET_CURRENT_USER(state, payload) {
state.c = payload
}

export default {
SET_CURRENT_USER,
}
```

Can be refactored into:

```javascript
import { mutationFactory } from 'vuex-factories'

export default mutationFactory({
SET_CURRENT_USER: [{ key: 'SET_CURRENT_USER' }],
})
```

## Typescript Support

Both factory functions support typescript.

In the case of the actionFactory this has somewhat reduced efficacy because vuex itself is not able to check typings using the dispatch and commit callers. To type the actionFactory you can pass two generics to it representing State and RootState, just like you would the regular vuex interfaces: `actionFactory<State,RootState>({...})`.

For the mutationFactory typescript offers validation of the state keys, which is quite useful. To do use this feature simply pass the typing of your state as a generic to the mutationFactory: `mutationFactory<State>({...})`
18 changes: 9 additions & 9 deletions src/index.ts
Expand Up @@ -9,7 +9,7 @@ export function actionFactory<S = any, R = any>(
return reduceToDict(
Object.entries(actions).map(([name, options]) => [
name,
(context: ActionContext<any, any>, payload?: any): void => {
(context: ActionContext<S, R>, payload?: any): void => {
if (!options.length)
throw new Error(
`[vuex-factories] options array is empty for action ${name}`,
Expand All @@ -21,9 +21,9 @@ export function actionFactory<S = any, R = any>(
const actionPayload =
typeof value === 'function'
? value(payload)
: typeof payload !== 'undefined'
? payload
: value
: typeof value !== 'undefined'
? value
: payload
if (typeof dispatch === 'string' && dispatch.trim()) {
context.dispatch(dispatch, actionPayload, {
root: Boolean(/\//.exec(dispatch)),
Expand All @@ -44,7 +44,7 @@ export function actionFactory<S = any, R = any>(
)
}

export function mutationFactory<S>(mutations: {
export function mutationFactory<S = any>(mutations: {
[k: string]: { key: S extends object ? keyof S : string; value?: any }[]
}): Dictionary<Mutation<S>> {
return reduceToDict(
Expand All @@ -67,12 +67,12 @@ export function mutationFactory<S>(mutations: {
key,
typeof value === 'function'
? value(payload)
: typeof payload !== 'undefined'
? payload
: value,
: typeof value !== 'undefined'
? value
: payload,
)
})
},
]),
) as Dictionary<Mutation<S>>
)
}

0 comments on commit 7b07cff

Please sign in to comment.