Skip to content

Commit

Permalink
docs: add more API docs
Browse files Browse the repository at this point in the history
  • Loading branch information
cray0000 committed Jul 6, 2024
1 parent 161a8a9 commit 976f354
Show file tree
Hide file tree
Showing 10 changed files with 367 additions and 19 deletions.
39 changes: 39 additions & 0 deletions docs/api/observer-hoc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# observer() HOC

The `observer()` Higher-Order Component (HOC) is used to make React components reactive to changes in TeamPlay signals.

## Syntax

```javascript
observer(Component)
```

## Parameters

- `Component`: The React component to be made reactive.

## Return Value

Returns a new component that is reactive to TeamPlay signals.

## Example

```javascript
import { observer, $ } from 'teamplay'

const UserProfile = observer(({ userId }) => {
const $user = $.users[userId]
return <div>{$user.name.get()}</div>
})
```

## Features

1. **Reactivity**: Components wrapped with `observer()` will automatically re-render when any TeamPlay signal they use changes.

2. **Suspense Integration**: `observer()` automatically wraps the component in a Suspense boundary, handling loading states for asynchronous operations.

## Notes

- Always wrap components that use TeamPlay signals with `observer()` to ensure they update when data changes.
- `observer()` optimizes re-renders by only updating when the specific data used in the component changes.
58 changes: 58 additions & 0 deletions docs/api/query-signals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Query Signals

Query signals in TeamPlay represent the result of a query on a collection. They are created using the `sub()` function or `useSub()` hook with query parameters.

## Creating a Query Signal

```javascript
const $activeUsers = await sub($.users, { status: 'active' })
```

## Properties and Methods

### ids

A signal containing an array of IDs for the documents in the query result.

```javascript
const userIds = $activeUsers.ids.get()
```

### map(callback)

Maps over the documents in the query result.

```javascript
const userNames = $activeUsers.map($user => $user.name.get())
```

### reduce(callback, initialValue)

Reduces the documents in the query result to a single value.

```javascript
const totalAge = $activeUsers.reduce(($user, total) => total + $user.age.get(), 0)
```

### find(predicate)

Finds the first document in the query result that satisfies the predicate.

```javascript
const $firstAdminUser = $activeUsers.find($user => $user.role.get() === 'admin')
```

## Iteration

Query signals are iterable, allowing you to use them in `for...of` loops:

```javascript
for (const $user of $activeUsers) {
console.log($user.name.get())
}
```

## Notes

- Query signals are reactive. Changes to the underlying data or to the query result will automatically update components using the query signal.
- The documents within a query signal are themselves signals, allowing for nested reactivity.
37 changes: 34 additions & 3 deletions docs/api/root-signal.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,41 @@ const $userName = $.users.userId.name
const $sessionData = $._session
```

## Simplifications

### Private '_session' Collection

For convenience, TeamPlay allows you to access the private '_session' collection without the underscore:

```javascript
// These are equivalent:
const $sessionData1 = $._session
const $sessionData2 = $.session
```

This simplification makes it easier to work with session data without constantly typing the underscore.

### Destructuring Assignment Simplification

When destructuring properties from a signal object, TeamPlay provides a convenient shorthand for properties starting with '$'. The '$' is automatically removed from the property name:

```javascript
const { $name, $age } = $({ name: 'John', age: 20 })

// This is equivalent to:
const { name: $name, age: $age } = $({ name: 'John', age: 20 })
```

This simplification allows for more concise and readable code when working with multiple signals from an object.

## Notes

- Public collections typically start with a lowercase letter (e.g., `users`, `posts`).
- Private collections start with an underscore (e.g., `_session`).
- Private collections start with an underscore or dollar sign (e.g., `_session`, `$page`).
- The root signal is available globally in your application after setting up TeamPlay.

Remember that accessing a signal doesn't automatically fetch or subscribe to the data. To actually get the data you have to first subscribe to it with `sub()` function and then get it from the signal with `.get()`.
- Remember that accessing a signal doesn't fetch or subscribe to the data. To actually retrieve or subscribe to the data, you need to use methods like `.get()` or the `sub()` function.
- If you need to access an actual property named '$', use '$$' instead. For example:
```javascript
const { $$specialProp } = $({ $specialProp: 'value' })
```
This is a rare case and is only needed if your data actually contains properties starting with '$'.
67 changes: 67 additions & 0 deletions docs/api/server-side.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Server-side API

TeamPlay provides a server-side API for setting up the backend and handling connections. This API is typically used in your server setup code.

## createBackend()

Creates a new TeamPlay backend instance.

```javascript
import { createBackend } from 'teamplay/server'

const backend = createBackend()
```

## initConnection(backend, options)

Initializes the connection handler for WebSocket connections.

```javascript
import { initConnection } from 'teamplay/server'

const { upgrade } = initConnection(backend, options)
```

### Parameters

- `backend`: The TeamPlay backend instance created with `createBackend()`.
- `options` (optional): An object with the following properties:
- `fetchOnly` (default: `true`): If true, server-side subscriptions are not reactive.

### Return Value

Returns an object with an `upgrade` function to be used with a Node.js HTTP server.

## Usage Example

```javascript
import http from 'http'
import { createBackend, initConnection } from 'teamplay/server'

const server = http.createServer()
const backend = createBackend()
const { upgrade } = initConnection(backend)

server.on('upgrade', upgrade)

server.listen(3000, () => {
console.log('Server started on port 3000')
})
```

## Additional Exports

TeamPlay's server module also re-exports some utilities:

- `ShareDB`: The underlying ShareDB library.
- `mongo`, `mongoClient`, `createMongoIndex`: MongoDB utilities.
- `redis`, `redlock`: Redis utilities.
- `sqlite`: SQLite utility.

These can be imported from `teamplay/server` if needed for advanced configurations.

## Notes

- The server-side API is designed to work with Node.js HTTP servers.
- For production use, it's recommended to use MongoDB by setting the `MONGO_URL` environment variable.
- When deploying to a cluster with multiple instances, set the `REDIS_URL` environment variable for proper scaling.
69 changes: 69 additions & 0 deletions docs/api/signal-methods.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Signal Methods

TeamPlay signals come with a set of methods for interacting with the data they represent. These methods are available on all signals, whether they're created using `$()`, accessed through the root signal `$`, or returned by `sub()` or `useSub()`.

## get()

Retrieves the current value of the signal.

```javascript
const value = $signal.get()
```

## set(value)

Updates the value of the signal.

```javascript
await $signal.set(newValue)
```

Note: `set()` is asynchronous and returns a Promise.

## del()

Deletes the value of the signal or removes an item from an array.

```javascript
await $signal.del()
```

Note: `del()` is asynchronous and returns a Promise.

## push(value)

Adds a value to the end of an array signal.

```javascript
await $signal.push(newItem)
```

## pop()

Removes and returns the last item from an array signal.

```javascript
const lastItem = await $signal.pop()
```

## increment(value)

Increments a numeric signal by the specified value (or by 1 if no value is provided).

```javascript
await $signal.increment(5)
```

## add(value)

Adds a new item to a collection signal, automatically generating a unique ID.

```javascript
const newId = await $signal.add({ name: 'New Item' })
```

## Notes

- All methods that modify data (`set()`, `del()`, `push()`, `pop()`, `increment()`, `add()`) are asynchronous and return Promises. This ensures data consistency with the server.
- The `get()` method is synchronous and returns the current local value of the signal.
- These methods can be chained on nested signals, e.g., `$.users[userId].name.set('New Name')`.
39 changes: 39 additions & 0 deletions docs/api/sub-function.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# sub() Function

The `sub()` function is used to subscribe to data from the server in TeamPlay. It can be used to subscribe to individual documents or to query multiple documents.

## Syntax

```javascript
sub(signal, [queryParams])
```

## Parameters

- `signal`: A signal representing the collection or document to subscribe to.
- `queryParams` (optional): An object containing query parameters when subscribing to multiple documents.

## Return Value

Returns a Promise that resolves to a signal representing the subscribed data.

## Examples

### Subscribing to a single document

```javascript
const $user = await sub($.users[userId])
console.log($user.name.get())
```

### Subscribing to a query (multiple documents)

```javascript
const $activeUsers = await sub($.users, { status: 'active' })
```

## Notes

- The `sub()` function is asynchronous and returns a Promise.
- When used in React components, it's recommended to use the `useSub()` hook instead, which handles the asynchronous nature of subscriptions in a React-friendly way.
- Subscribed data is automatically kept in sync with the server.
42 changes: 42 additions & 0 deletions docs/api/use-sub-hook.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# useSub() Hook

The `useSub()` hook is a React hook that combines the functionality of the `sub()` function with React's component lifecycle. It's used to subscribe to TeamPlay data within React components.

## Syntax

```javascript
const $data = useSub(signal, [queryParams])
```

## Parameters

- `signal`: A signal representing the collection or document to subscribe to.
- `queryParams` (optional): An object containing query parameters when subscribing to multiple documents.

## Return Value

Returns a signal representing the subscribed data.

## Example

```javascript
import { observer, $, useSub } from 'teamplay'

const UserProfile = observer(({ userId }) => {
const $user = useSub($.users[userId])
return <div>{$user.name.get()}</div>
})
```

## Features

1. **Automatic Subscription Management**: `useSub()` handles subscribing when the component mounts and unsubscribing when it unmounts.

2. **Suspense Integration**: It works seamlessly with React Suspense, automatically handling loading states.

3. **Reactivity**: Changes to the subscribed data will cause the component to re-render.

## Notes

- `useSub()` should be used within components wrapped with `observer()` to ensure proper reactivity.
- It's designed to work with React's rules of hooks, so it should not be used in conditional statements.
20 changes: 5 additions & 15 deletions docs/guide/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -37,25 +37,15 @@ server.on('upgrade', upgrade) // Node's 'http' server instance

## Database Configuration

- For production use, it's recommended to use MongoDB. It will be automatically used if you set the environment variable `MONGO_URL`.
- When deploying to a cluster with multiple instances, you also have to set the environment variable `REDIS_URL` (Redis).
By default no extra database setup is needed and the data is gonna be saved into an SQLite file `local.db` in the root of your project.

If `MONGO_URL` is not set, the alternative `mingo` mock is used instead, which persists data into an SQLite file `local.db` in the root of your project.
You can still use the MongoDB query syntax with aggregations which is emulated using [`mingo`](https://github.com/kofrasa/mingo).

- For production use, it's recommended to use [MongoDB](https://mongodb.com). It will be automatically used if you set the environment variable `MONGO_URL`.
- When deploying to a cluster with multiple instances, you also have to provide the environment variable `REDIS_URL` ([Redis](https://redis.io)).

:::note
TeamPlay's `createBackend()` is a wrapper around creating a [ShareDB's backend](https://share.github.io/sharedb/api/backend). You can instead manually create a ShareDB backend yourself and pass it to `initConnection()`. `ShareDB` is re-exported from `teamplay/server`, you can get it as `import { ShareDB } from 'teamplay/server'`.
:::

## `initConnection(backend, options)`

The `initConnection` function accepts the following parameters:

- `backend`: ShareDB backend instance
- `options`:
- `fetchOnly` (default: `true`): By default, all subscriptions on the server are not reactive. This is strongly recommended. If you need the subscriptions to reactively update data whenever it changes (the same way as they work on client-side), pass `{ fetchOnly: false }`.

```js
const { upgrade } = initConnection(backend, { fetchOnly: false })
```

Now that you have TeamPlay installed and configured, you're ready to start using it in your application!
Loading

0 comments on commit 976f354

Please sign in to comment.