Skip to content

Commit

Permalink
docs: Rearrange homepage (#1352)
Browse files Browse the repository at this point in the history
  • Loading branch information
ntucker committed Oct 8, 2021
1 parent 2626e2f commit 404b2b0
Show file tree
Hide file tree
Showing 28 changed files with 778 additions and 230 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ Asynchronous dynamic data at scale. Performance, data integrity, and typing for

<div align="center">

## 🌎 [Website](https://resthooks.io)

</div>

<div align="center">

**[📖Read The Docs](https://resthooks.io/docs)** &nbsp;|&nbsp; [🏁Getting Started](https://resthooks.io/docs/getting-started/installation) &nbsp;|&nbsp;
[🎮Todo Demo](https://github.com/coinbase/rest-hooks/tree/master/examples/todo-app) &nbsp;|&nbsp;
[🎮Github Demo](https://github.com/coinbase/rest-hooks/tree/master/examples/github-app)
Expand Down
59 changes: 57 additions & 2 deletions docs/getting-started/expiry-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ it will still refresh the data if old enough.
To explain these concepts we'll be faking an endpoint that gives us the current time so it is easy to tell how stale it is.

```tsx title="lastUpdated.ts"
const mockFetch = ({ id, delay = 150 }) =>
const mockLastUpdated = ({ id, delay = 150 }) =>
new Promise(resolve =>
setTimeout(
() =>
Expand All @@ -32,7 +32,7 @@ class TimedEntity extends Entity {
};
}

const lastUpdated = new Endpoint(mockFetch, { schema: TimedEntity });
const lastUpdated = new Endpoint(mockLastUpdated, { schema: TimedEntity });
```

## Expiry status
Expand All @@ -55,6 +55,8 @@ no components care about this data no action will be taken.

## Expiry Time

### Endpoint.dataExpiryTime

[Endpoint.dataExpiryTime](../api/Endpoint#dataexpirylength) sets how long (in miliseconds) it takes for data
to transition from 'fresh' to 'stale' status. Try setting it to a very low number like '50'
to make it becomes stale almost instantly; or a very large number to stay around for a long time.
Expand Down Expand Up @@ -104,6 +106,59 @@ render(<Navigator />);

</HooksPlayground>

### Endpoint.invalidIfStale

[Endpoint.invalidIfStale](../api/Endpoint#invalidifstale) eliminates the `stale` status, making data
that expires immediately be considered 'invalid'.

This is demonstrated by the component suspending once its data goes stale. If the data is still
within the expiry time it just continues to display it.

<HooksPlayground>

```tsx
const lastUpdated = lastUpdated.extend({
invalidIfStale: true,
dataExpiryLength: 5000,
});

function TimePage({ id }) {
const { updatedAt } = useResource(lastUpdated, { id });
return (
<div>
API Time:{' '}
<time>
{Intl.DateTimeFormat('en-US', { timeStyle: 'long' }).format(updatedAt)}
</time>
</div>
);
}

function Navigator() {
const [id, setId] = React.useState('1');
const handleChange = e => setId(e.currentTarget.value);
return (
<div>
<div>
<button value="1" onClick={handleChange}>
First
</button>
<button value="2" onClick={handleChange}>
Second
</button>
</div>
<TimePage id={id} />
<div>
Current Time: <CurrentTime />
</div>
</div>
);
}
render(<Navigator />);
```

</HooksPlayground>

## Force refresh

[Controller.fetch](../api/Controller#fetch) can be used to trigger a fetch while still showing
Expand Down
45 changes: 39 additions & 6 deletions docs/getting-started/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,60 @@
title: Validation
---

```ts
import HooksPlayground from '@site/src/components/HooksPlayground';

<HooksPlayground>

```tsx
class ArticlePreview extends Entity {
readonly id: string = '';
readonly title: string = '';

pk() { return this.id; }
static key() { return 'Article'; }
pk() {
return this.id;
}
static key() {
return 'Article';
}
}
const articleList = new Endpoint(fetchList, { schema: [ArticlePreview] });
const mockArticleList = mockFetch(() => [
{ id: '1', title: 'first' },
{ id: '2', title: 'second' },
]);
const articleList = new Endpoint(mockArticleList, { schema: [ArticlePreview] });

class ArticleFull extends ArticlePreview {
readonly content: string = '';
readonly createdAt: Date = new Date(0);

static schema = {
createdAt: Date,
}
};

static validate(processedEntity) {
if (!Object.hasOwn(processedEntity, 'content')) return 'Missing content';
}
}
const articleDetail = new Endpoint(fetchDetail, { schema: ArticleFull });
const mockArticleDetail = mockFetch(
({ id }) =>
({
'1': { id: '1', title: 'first', content: 'long' },
'2': { id: '2', title: 'second', content: 'short' },
}[id]),
);
const articleDetail = new Endpoint(mockArticleDetail, {
schema: ArticleFull,
key({ id }) {
return `article ${id}`;
},
});

function ArticlePage() {
const article = useResource(articleDetail, { id: '1' });
return <div>{article.title}</div>;
}

render(<ArticlePage />);
```

</HooksPlayground>
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
"async",
"data fetching",
"data cache",
"api client",
"api",
"normalized cache",
"swr",
Expand Down
1 change: 1 addition & 0 deletions packages/rest-hooks/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
"redux",
"data fetching",
"data cache",
"api client",
"api",
"normalized cache",
"swr",
Expand Down
2 changes: 1 addition & 1 deletion website/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ module.exports = {
},
{
to: 'docs/rest/usage',
label: 'REST / CRUD',
label: 'REST',
position: 'left',
},
{
Expand Down
8 changes: 7 additions & 1 deletion website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,13 @@
"deploy": "USE_SSH=true docusaurus deploy",
"docusaurus": "docusaurus"
},
"devDependencies": {},
"devDependencies": {
"@docusaurus/module-type-aliases": "^2.0.0-beta.6",
"@tsconfig/docusaurus": "^1.0.4",
"@types/react": "^17.0.27",
"@types/react-helmet": "^6.1.3",
"@types/react-router-dom": "^5.3.1"
},
"dependencies": {
"@anansi/webpack-config": "^9.1.0",
"@docusaurus/core": "^2.0.0-beta.6",
Expand Down
41 changes: 41 additions & 0 deletions website/src/components/Demo/CodeEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import React, { useContext, memo } from 'react';

import CodeProvider from './CodeProvider';
import HooksPlayground from '../HooksPlayground';
import CodeTabContext from './CodeTabContext';

// eslint-disable-next-line react/display-name
const DemoPlayground = memo(() => {
const { selectedValue, values } = useContext(CodeTabContext);

return (
<>
{values.map(({ value, code }) => (
<HooksPlayground
groupId="homepage-demo"
row
key={value}
hidden={value !== selectedValue}
>
{code}
</HooksPlayground>
))}
</>
);
});

interface Props {
codes: { label: string; value: string; code: string }[];
}

export default function CodeEditor({ codes }: Props) {
return (
<CodeProvider
defaultValue={codes[0].value}
groupId="protocol"
values={codes}
>
<DemoPlayground />
</CodeProvider>
);
}
50 changes: 50 additions & 0 deletions website/src/components/Demo/CodeProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React, { memo, useState } from 'react';
import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext';

import CodeTabContext from './CodeTabContext';

interface Props<V extends { label: string; value: string }[]> {
values: V;
groupId?: string | null;
defaultValue: V[number]['value'];
children: React.ReactNode;
}

export function CodeProvider<V extends { label: string; value: string }[]>({
defaultValue,
groupId = null,
values,
children,
}: Props<V>) {
const { tabGroupChoices, setTabGroupChoices } = useUserPreferencesContext();
const [selectedValue, setLocalSelectedValue] = useState(defaultValue);

if (groupId != null) {
const choice = tabGroupChoices[groupId];
if (
choice != null &&
choice !== selectedValue &&
values.find(({ value }) => value === choice)
) {
setLocalSelectedValue(choice as any);
}
}

const setSelectedValue = (selectedTabValue: string) => {
setLocalSelectedValue(selectedTabValue);

if (groupId != null) {
setTabGroupChoices(groupId, selectedTabValue);
}
};

const value = {
selectedValue,
values,
setSelectedValue,
};
return (
<CodeTabContext.Provider value={value}>{children}</CodeTabContext.Provider>
);
}
export default memo(CodeProvider);
10 changes: 10 additions & 0 deletions website/src/components/Demo/CodeTabContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { createContext } from 'react';

const CodeTabContext = createContext({
selectedValue: '',
setSelectedValue: (value: string): void => {
throw new Error('No Tab provider');
},
values: [],
});
export default CodeTabContext;
27 changes: 0 additions & 27 deletions website/src/components/Demo/EndpointDemo.js

This file was deleted.

35 changes: 0 additions & 35 deletions website/src/components/Demo/GraphQLDemo.js

This file was deleted.

Loading

0 comments on commit 404b2b0

Please sign in to comment.