# ShareDB Hooks

StartupJS has convenient tools for working with databases. You can use custom hooks to work with ShareDB.

## What are they doing

1. Provide reactive interaction of React applications with ShareDB

1. Use [Racer](https://derbyjs.com/docs/derby-0.10/models) to add a model to your application to perform any data manipulation

1.	The model works like a global singleton state, so you can use it as a replacement for other state managers like Redux or Mobx

1. Make the component render reactive, as it is in Mobx, whenever any of the model data that you used in the render is altered

## useDoc(collection, docId)

Enables subscription to a specific Mongo document by its `id`.

**Arguments**

`collection (String)`: collection name (required)

`docId (String)`: document id (required)

**Returns**

`[doc, $doc]`, where:

  * `doc (Object)`: document

  * `$doc (Model)`: a model that points to a specific document at `collection.docId`

**Example**

```jsx
import React from 'react'
import { observer, useDoc } from 'startupjs'
import { Card, Span, Button } from '@startupjs/ui'

export default observer(function TodoCard ({
  todoId = 'DUMMY_ID'
}) {
  let [todo, $todo] = useDoc('todos', todoId)

  return (
    <Card>
      <Span>{todo.title}</Span>
      <Button
        size='s'
        onPress={() => $todo.del()}
      >
        Delete
      </Button>
    </Card>
  )
```

**IMPORTANT**: The document id in Mongo is stored inside the `_id` field. However, when it gets within the model, it is replaced with the `id` field, and vice versa.

## useQuery(collection, query)

Enables subscription using a Mongo request

**Arguments**

`collection (String)`: collection name (required)

`query (Object)`: query (regular expression, `$count` and `$aggregate` are supported) (required)

**Returns**

`[docs, $docs]`, where:

  * `docs (Array)`: an array of documents

  * `$docs (Model)`: a model that points to `collection`

**Example**

```jsx
let [todos, $todos] = useQuery('todos', { completed: false })
// We will receive all outstanding tasks
```

**IMPORTANT**: the `$docs` model, returned from the hook, points to the global collection path. You can use it to easily access a document with a specific `id`:

```jsx
let [todos, $todos] = useQuery('todos', { completed: false })

for (let todo of todos) {
  $todos.at(todo.id).setEach({
    completed: true,
    updatedAt: Date.now()
  })
}
```

## useQueryIds(collection, ids, options)

Enables subscription to the documents in a collection using their ids.

**Arguments**

`collection (String)`: collection name (required)

`ids (Array)`: an array of `id` strings

`options (Object)`:
  * `reverse (Boolean)`: will change the order of documents in the resulting array

**Returns**

`[docs, $docs]`, where:

  * `docs (Array)`: an array of documents

  * `$docs (Model)`: a model that points to the `collection`

**Example**

```jsx
export default observer(function TodosList ({ todoIds }) {
  let [todos, $todos] = useQueryIds('todos', todoIds)

  return (
    <Div>{todos.map(i => i.title).join(' ,')}</Div>
  )
})
```

## useQueryDoc(collection, query)

Enables subscription to a document with the use of request. This hook is similar to `useDoc()`, but it takes a `query` object instead of a specific `id`.
`$limit: 1` and `$sort: {createdAt: -1}` are added to the `query` automatically (if not redefined).

**Arguments**

`collection (String)`: collection name (required)

`query (Object)`: the query object, as in `useQuery()`

**Returns**

`[doc, $doc]`, where:

  * `doc (Object)`: document

  * `$doc (Model)`: a model that points to a specific document at `collection.docId`

**Example**

```jsx
export default observer(function LatestTodo ({ userId }) {
  // { $sort: { createdAt: -1 }, $limit: 1 }
  // is added automatically to the request, so the newest player will be returned
  // It works reactively, so whenever the user creates a new task, you get new data and a model.
  let [latestTodo, $latestTodo] = useQueryDoc('todos', { userId })
  if (!latestTodo) return null // <Loading />

  return (
    <div>Your last added todo: {latestTodo.title}</div>
  )
})
```

## useLocal(path)

Enables subscription to data at the specified path that you already have in your local model.

Most often, you will use this hook to subscribe to private collections such as `_page` or `_session`. This is very useful when you want to share a state between multiple components.

You can also subscribe to a path from a public collection, for example, if you want to work with some nested value of a specific document that you have already subscribed to.

**Arguments**

`path (String)`: path to the document (required)

**Returns**

`[value, $value]`, where:

  * `value (any)`: data located at `path`

  * `$value (Model)`: model pointing to `path`

**Example**

```jsx
const SIDEBAR_OPENED = '_page.Sidebar.opened'

const Topbar = observer(() => {
  let [sidebarOpened, $sidebarOpened] = useLocal(SIDEBAR_OPENED)
  return <>
    <button
      onClick={() => $sidebarOpened.set(!sidebarOpened)}
    >Toggle Sidebar</button>
  </>
})

const Sidebar = observer(() => {
  let [sidebarOpened] = useLocal(SIDEBAR_OPENED)
  return sidebarOpened ? <p>Sidebar</p> : null
})

const App = observer(() => {
  return <>
    <Topbar />
    <Sidebar />
  </>
})
```

## useSession(path)

A convenient way to access local collection `_session`

```jsx
let [userId, $userId] = useSession('userId')
// This is the same as:
let [userId, $userId] = useLocal('_session.userId')
```

## usePage(path)

A convenient way to access local collection `_page`

```jsx
let [game, $game] = usePage('game')
// This is the same as:
let [game, $game] = useLocal('_page.game')
```

## useValue(defaultValue)

`Observable` is an alternative to `useState`

```jsx
const [inputValue, $inputValue] = useValue('')

<TextInput
  value={inputValue}
  onChangeText={value => $inputValue.set(value)}
/>
```

## useModel(path)

Returns the model at `path`. If `path` is not specified, then the model bound to the root path will be returned.

```jsx
import React from 'react'
import {render} from 'react-dom'
import {observer, useModel, useLocal} from 'startupjs'

const Main = observer(() => {
  return (
    <div style={{display: 'flex'}}>
      <Content />
      <Sidebar />
    </div>
  )
})

const Content = observer(() => {
  let $showSidebar = useModel('_page.Sidebar.show')

  // sidebar will be opened without triggering rerendering of the <Content /> component (this component)
  return (
    <div>
      <p>I am Content</p>
      <button onClick={() => $showSidebar.setDiff(true)}>Open Sidebar</button>
    </div>
  )
})

const Sidebar = observer(() => {
  let [show, $show] = useLocal('_page.Sidebar.show')
  if (!show) return null
  return (
    <div>
      <p>I am Sidebar</p>
      <button onClick={() => $show.del()}>Close</button>
    </div>
  )
})

render(<Main />, document.body.appendChild(document.createElement('div')))
```
