Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add configuration to getDefaultManagers() #3161

Merged
merged 1 commit into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/pink-dodos-protect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@data-client/react': patch
'@data-client/core': patch
---

Add jsdocs to IdlingNetworkManager
30 changes: 30 additions & 0 deletions .changeset/witty-papayas-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
'@data-client/react': patch
---

Add configuration to [getDefaultManagers()](https://dataclient.io/docs/api/getDefaultManagers)

```ts
// completely remove DevToolsManager
const managers = getDefaultManagers({ devToolsManager: null });
```

```ts
// easier configuration
const managers = getDefaultManagers({
devToolsManager: {
// double latency to help with high frequency updates
latency: 1000,
// skip websocket updates as these are too spammy
predicate: (state, action) =>
action.type !== actionTypes.SET_TYPE || action.schema !== Ticker,
}
});
```

```ts
// passing instance allows us to use custom classes as well
const managers = getDefaultManagers({
networkManager: new CustomNetworkManager(),
});
```
4 changes: 2 additions & 2 deletions docs/core/api/DataProvider.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ be useful for testing, or rehydrating the cache state when using server side ren

### managers?: Manager[] {#managers}

List of [Manager](./Manager.md#provided-managers)s use. This is the main extensibility point of the provider.
List of [Manager](./Manager.md)s use. This is the main extensibility point of the provider.

`getDefaultManagers()` can be used to extend the default managers.
[getDefaultManagers()](./getDefaultManagers.md) can be used to extend the default managers.

Default Production:

Expand Down
19 changes: 5 additions & 14 deletions docs/core/api/DevToolsManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ browser to get started.
[Arguments](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md)
to send to redux devtools.

For example, we can enable the `trace` option to help track down where actions are dispatched from.
For example, we can enable the [trace](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#trace) option to help track down where actions are dispatched from.

```tsx title="index.tsx"
import {
Expand All @@ -37,19 +37,10 @@ import {
} from '@data-client/react';
import ReactDOM from 'react-dom';

const managers =
process.env.NODE_ENV !== 'production'
? [
// highlight-start
new DevToolsManager({
trace: true,
}),
// highlight-end
...getDefaultManagers().filter(
manager => manager.constructor.name !== 'DevToolsManager',
),
]
: getDefaultManagers();
const managers = getDefaultManagers({
// highlight-next-line
devToolsManager: { trace: true },
});

ReactDOM.createRoot(document.body).render(
<DataProvider managers={managers}>
Expand Down
6 changes: 3 additions & 3 deletions docs/core/api/Manager.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import useBaseUrl from '@docusaurus/useBaseUrl';

# Manager

Managers are singletons that orchestrate the complex asynchronous behavior of `Reactive Data Client`.
Several managers are provided by `Reactive Data Client` and used by default; however there is nothing
`Managers` are singletons that orchestrate the complex asynchronous behavior of <abbr title="Reactive Data Client">Data Client</abbr>.
Several managers are provided by <abbr title="Reactive Data Client">Data Client</abbr> and used by default; however there is nothing
stopping other compatible managers to be built that expand the functionality. We encourage
PRs or complimentary libraries!

While managers often have complex internal state and methods - the exposed interface is quite simple.
Because of this, it is encouraged to keep any supporting state or methods marked at protected by
typescript. Managers have three exposed pieces - the constructor to build initial state and
typescript. `Managers` have three exposed pieces - the constructor to build initial state and
take any parameters; a simple cleanup() method to tear down any dangling pieces like setIntervals()
or unresolved Promises; and finally getMiddleware() - providing the mechanism to hook into
the flux data flow.
Expand Down
2 changes: 1 addition & 1 deletion docs/core/api/NetworkManager.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ it is able to dedupe identical requests if they are made using the throttle flag

## Members

### constructor(dataExpiryLength = 60000, errorExpiryLength = 1000) {#constructor}
### constructor(\{ dataExpiryLength = 60000, errorExpiryLength = 1000 }) {#constructor}

Arguments represent the default time (in miliseconds) before a resource is considered 'stale'.

Expand Down
128 changes: 128 additions & 0 deletions docs/core/api/getDefaultManagers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
---
title: getDefaultManagers() - Configuring managers for DataProvider
sidebar_label: getDefaultManagers
---

import StackBlitz from '@site/src/components/StackBlitz';

# getDefaultManagers()

`getDefaultManagers` returns an Array of [Managers](./Manager.md) to be sent to [&lt;DataProvider />](./DataProvider.md).

This makes it simple to configure and add custom [Managers](./Manager.md), while remaining robust against
any potential changes to the default managers.

Currently returns \[[DevToolsManager](./DevToolsManager.md)\*, [NetworkManager](./NetworkManager.md), [SubscriptionManager](./SubscriptionManager.md)\].

\*(`DevToolsManager` is excluded in production builds.)

## Usage

```tsx
import {
DevToolsManager,
DataProvider,
getDefaultManagers,
} from '@data-client/react';
import ReactDOM from 'react-dom';

// highlight-start
const managers = getDefaultManagers({
// set fallback expiry time to an hour
networkManager: { dataExpiryLength: 1000 * 60 * 60 },
});
// highlight-end

ReactDOM.createRoot(document.body).render(
<DataProvider managers={managers}>
<App />
</DataProvider>,
);
```

See [DataProvider](./DataProvider.md) for details on usage in different environments.

## Arguments

Each argument represents a configuration of the manager. It can be of three possible types:

- Any plain object is used as options to be sent to the manager's constructor.
- An instance of the manager to be used directly.
- `null`. When sent will exclude the manager.

```ts
getDefaultManagers({
devToolsManager: { trace: true },
networkManager: new NetworkManager({ errorExpiryLength: 1 }),
subscriptionManager: null,
});
```

### networkManager

:::note

`null` is not allowed here since NetworkManager is required

:::

`dataExpiryLength` is used as a fallback when an Endpoint does not have [dataExpiryLength](https://dataclient.io/docs/concepts/expiry-policy#endpointdataexpirylength) defined.

`errorExpiryLength` is used as a fallback when an Endpoint does not have [errorExpiryLength](https://dataclient.io/docs/concepts/expiry-policy#endpointerrorexpirylength) defined.

### devToolsManager

[Arguments](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md)
to send to redux devtools.

### subscriptionManager

A class that implements `SubscriptionConstructable` like [PollingSubscription](./PollingSubscription.md)

## Examples

### Tracing actions

For example, we can enable the [trace](https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#trace) option to help track down where actions are dispatched from. This has a large performance impact, so it is normally disabled.

```ts
const managers = getDefaultManagers({
// highlight-next-line
devToolsManager: { trace: true },
});
```

### Manager inheritance

Sending manager instances allows us to customize managers using inheritance.

```ts
import { IdlingNetworkManager } from '@data-client/react';

const managers = getDefaultManagers({
networkManager: new IdlingNetworkManager(),
});
```

`IdlingNetworkManager` can prevent stuttering by delaying [sideEffect](/rest/api/Endpoint#sideeffect)-free (read-only/GET) fetches
until animations are complete. This works in web using [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback), and react native using InteractionManager.runAfterInteractions.

### Disabling

Using `null` will remove managers completely. [NetworkManager](./NetworkManager.md) cannot be removed this way.

```ts
const managers = getDefaultManagers({
devToolsManager: null,
subscriptionManager: null,
});
```

Here we disable every manager except [NetworkManager](./NetworkManager.md).

### Coin App

New prices are streamed in many times a second; to reduce devtool spam, we set it
to ignore [SET](./Controller.md#set) actions for `Ticker`.

<StackBlitz app="coin-app" file="src/index.tsx,src/resources/StreamManager.ts,src/getManagers.ts" height="600" />
2 changes: 1 addition & 1 deletion docs/core/concepts/managers.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,4 @@ with `event.data`.

### Coin App

<StackBlitz app="coin-app" file="src/index.tsx,src/resources/Ticker.ts,src/pages/AssetDetail/AssetPrice.tsx,src/resources/StreamManager.ts" height="600" />
<StackBlitz app="coin-app" file="src/getManagers.tsx,src/resources/Ticker.ts,src/pages/AssetDetail/AssetPrice.tsx,src/resources/StreamManager.ts" height="600" />
17 changes: 4 additions & 13 deletions docs/core/getting-started/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,19 +97,10 @@ import {
} from '@data-client/react';
import ReactDOM from 'react-dom';

const managers =
process.env.NODE_ENV !== 'production'
? [
// highlight-start
new DevToolsManager({
trace: true,
}),
// highlight-end
...getDefaultManagers().filter(
manager => manager.constructor.name !== 'DevToolsManager',
),
]
: getDefaultManagers();
const managers = getDefaultManagers({
// highlight-next-line
devToolsManager: { trace: true },
});

ReactDOM.createRoot(document.body).render(
<DataProvider managers={managers}>
Expand Down
21 changes: 21 additions & 0 deletions examples/coin-app/src/getManagers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getDefaultManagers, actionTypes } from '@data-client/react';
import StreamManager from 'resources/StreamManager';
import { Ticker } from 'resources/Ticker';

export default function getManagers() {
return [
new StreamManager(
() => new WebSocket('wss://ws-feed.exchange.coinbase.com'),
{ ticker: Ticker },
),
...getDefaultManagers({
devToolsManager: {
// double latency to help with high frequency updates
latency: 1000,
// skip websocket updates as these are too spammy
predicate: (state, action) =>
action.type !== actionTypes.SET_TYPE || action.schema !== Ticker,
},
}),
];
}
48 changes: 3 additions & 45 deletions examples/coin-app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,8 @@ import {
JSONSpout,
appSpout,
} from '@anansi/core';
import {
useController,
AsyncBoundary,
getDefaultManagers,
DevToolsManager,
NetworkManager,
actionTypes,
} from '@data-client/react';
import StreamManager from 'resources/StreamManager';
import { Ticker } from 'resources/Ticker';
import { useController, AsyncBoundary } from '@data-client/react';
import getManagers from 'getManagers';

import App from './App';
import { createRouter } from './routing';
Expand All @@ -28,17 +20,7 @@ const app = (

const spouts = JSONSpout()(
documentSpout({ title: 'Coin App' })(
dataClientSpout({
getManagers: () => {
return [
new StreamManager(
() => new WebSocket('wss://ws-feed.exchange.coinbase.com'),
{ ticker: Ticker },
),
...getManagers(),
];
},
})(
dataClientSpout({ getManagers })(
routerSpout({
useResolveWith: useController,
createRouter,
Expand All @@ -47,28 +29,4 @@ const spouts = JSONSpout()(
),
);

function getManagers() {
const managers = getDefaultManagers().filter(
manager => manager.constructor.name !== 'DevToolsManager',
);
if (process.env.NODE_ENV !== 'production') {
const networkManager: NetworkManager | undefined = managers.find(
manager => manager instanceof NetworkManager,
) as any;
managers.unshift(
new DevToolsManager(
{
// double latency to help with high frequency updates
latency: 1000,
// skip websocket updates as these are too spammy
predicate: (state, action) =>
action.type !== actionTypes.SET_TYPE || action.schema !== Ticker,
},
networkManager && (action => networkManager.skipLogging(action)),
),
);
}
return managers;
}

export default floodSpouts(spouts);
5 changes: 3 additions & 2 deletions packages/core/src/manager/SubscriptionManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ export interface SubscriptionConstructable {
*
* @see https://dataclient.io/docs/api/SubscriptionManager
*/
export default class SubscriptionManager<S extends SubscriptionConstructable>
implements Manager<Actions>
export default class SubscriptionManager<
S extends SubscriptionConstructable = SubscriptionConstructable,
> implements Manager<Actions>
{
protected subscriptions: {
[key: string]: InstanceType<S>;
Expand Down
Loading
Loading