# Reedx

## Vantagens
- Like redux, porém com menos boilerplate e teorias.
- Sem necessidade de instalar vários packages.
- Sem reducers, actions, types, whatevers.
- Integra normalmente com as libs do Redux (middlewares, devtools, etc).

## Arquitetura
<p align="center"><img src="https://d26dzxoao6i3hh.cloudfront.net/items/132N310h2i3r3J2Y1d3a/reedx%20.png?v=a782bd63" height=700" /></p>

### Model
Um `model` é responsável por guardar dados e expor métodos. Não é possível modificar um model senão for atráves de um método (`reducer`) responsável por isso.

### Reducers
São os métodos responsáveis por modificar as propriedades base de um `model`. Um `reducer` pode modificar apenas o seu próprio `model`.

### Computed
Colocar muita lógica referente ao seu model nos reducers não é a melhor maneira de tornar seu código readable. Para isso existe as `computed properties`, que são propriedades não armazenadas na memória do seu model, mas que serão expostas ao conectar o mesmo em algum componente. Este conceito é algo muito parecido do que é possível com os seletores no Redux.

## Types

- `Model: { [key:string]: any }`
- `Action: Object<type: string, payload: any, error: boolean>`
- `Reducer: { [key: string]: (state: Model, action: Action) => void }`
- `Computed: { [key: string]: (state: Model, props: Object) => any }`

## API

### `model({ name, state, computed, reducers })`

#### params
- `name: string`
- `state: State`
- `computed: Object<Computed>`
- `reducers: Object<Reducers>`

#### exemplos
```js
import { model } from 'reedx'

const state = {
  entities: [],
  selectedId: ''
}

const reducers = {
  addUser(state, { payload: user }) {
    return Object.assign({}, state, { entities: [...state.entities, user] }),
  },

  selectUser(state, { payload: id }) {
    return Object.assign({}, state, { selectedId: id })
  }
}

export default model({ name: 'users', state, reducers })
```

ou usando as `computed properties`:

```js
import { model } from 'reedx'

const state = {
  entities: [],
  selectedId: ''
}

const computed = {
  user(state) {
    const { users } = state
    return users.entities.find(user => user.id === users.selectedId)
  }
}

const reducers = {
  addUser(state, { payload: user }) {
    return Object.assign({}, state, { entities: [...state.entities, user] }),
  },

  selectUser(state, { payload: id }) {
    return Object.assign({}, state, { selectedId: id })
  }
}

export default model({ name: 'users', state, computed, reducers })
```

---

### `propsFrom(...models)`

#### params
- `models: Model`

#### exemplos
```js
import React from 'react'
import { connect } from 'react-redux'
import { propsFrom } from 'reedx'

import { users } from './models'

const Users = ({ users, selectedId }) => (
  /* */
)

export default connect(propsFrom(users))(Users)
```

---

### `handlersFrom(...models)`

#### params
- `models: Model`

#### exemplo
```js
import React from 'react'
import { connect } from 'react-redux'
import { propsFrom, handlersFrom } from 'reedx'

import { users } from './models'

const Users = ({ users, selectedId, selectUser }) => (
  /* */
)

export default connect(propsFrom(users), handlersFrom(users))
```

### `pick(model, properties)`

#### params
- `model: Model`
- `properties: Array<string>`

#### exemplo
```js
import { model, propsFrom, pick } from 'reedx'

const users = model({
  name: 'users',
  state: {
    all: [],
    selectedId: null,
  }
})

const mapProps = propsFrom(
  pick(users, ['selectedId']))
)

console.log(mapProps({ users })) // { selectedId }
```

### `omit(model, properties)`

#### params
- `model: Model`
- `properties: Array<string>`

#### exemplo
```js
import { model, propsFrom, omit } from 'reedx'

const users = model({
  name: 'users',
  state: {
    all: [],
    selectedId: null,
  }
})

const mapProps = propsFrom(
  omit(users, ['selectedId']))
)

console.log(mapProps({ users })) // { all }
```

## Renomeando propriedades ao conectar

Algumas vezes você pode ter proprieades com o mesmo valor entre models diferentes e ter a necessidade de conectar os dois na mesma view. Neste cenário, você teria a última propriedade sobrescrevendo a primeira devido aos seus valores serem iguais. Para evitar isso, basta você setar o novo nome da sua variável usando `:` após a definição no método de `pick` ou `omit`

```js
import { model, propsFrom, pick } from 'reedx'

const users = model({
  name: 'users',
  state: {
    otherProp: null,
  }
})

const mapProps = propsFrom(
  pick(users, ['otherProp:prop'])
)

console.log(mapProps({ users })) // { prop }
```
