Skip to content

Commit

Permalink
docs(en): merging all conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
docschina-bot committed May 26, 2020
2 parents b2efab9 + f2edd6b commit fd6b2a7
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 15 deletions.
9 changes: 9 additions & 0 deletions docs/docs/api-reference/core/atom.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,20 @@ sidebar_label: atom()

通常,你需要使用以下 hook 来与 atom 搭配使用。

<<<<<<< HEAD
- [`useRecoilState()`](/docs/api-reference/core/useRecoilState):当你同时需要对 atom 进行读写时,使用此 hook。使用此 hook 会使组件订阅 atom。
- [`useRecoilValue()`](/docs/api-reference/core/useRecoilValue):当你仅需要读取 atom 时,使用此 hook。使用此 hook 会使组件订阅 atom。
- [`useSetRecoilState()`](/docs/api-reference/core/useSetRecoilState):当你仅需要写入 atom 时,使用此 hook。

在一些罕见的场景下,你需要在不订阅组件的情况下读取 atom 的值,请参考 [`useRecoilCallback()`](/docs/api-reference/core/useRecoilCallback)
=======
- [`useRecoilState()`](/docs/api-reference/core/useRecoilState): use this hook when you intend on both reading and writing to the atom. This hook subscribes the component to the atom.
- [`useRecoilValue()`](/docs/api-reference/core/useRecoilValue): use this hook when you intend on only reading the atom. This hook subscribes the component to the atom.
- [`useSetRecoilState()`](/docs/api-reference/core/useSetRecoilState): use this hook when you intend on only writing to the atom.
- [`useResetRecoilState()`](/docs/api-reference/core/useResetRecoilState): use this hook to reset an atom to its default value.

For rare cases where you need to read an atom's value without subscribing to the component, see [`useRecoilCallback()`](/docs/api-reference/core/useRecoilCallback).
>>>>>>> f2edd6bec9db35bd999e692b046556827f22f44e
### 示例

Expand Down
31 changes: 30 additions & 1 deletion docs/docs/api-reference/core/useRecoilValueLoadable.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,50 @@ title: useRecoilValueLoadable()
sidebar_label: useRecoilValueLoadable()
---

<<<<<<< HEAD
返回一个 `Loadable`.
=======
```jsx
function useRecoilValueLoadable<T>(state: RecoilValue<T>): Loadable<T>
```

Returns a `Loadable`.
>>>>>>> f2edd6bec9db35bd999e692b046556827f22f44e

该 hook 用来读取异步的 selector。使用此 hook 会使组件隐式地订阅给定的 state。

与 `useRecoilValue()` 不同,当从一个挂起的异步 selector 中读取值时,这个 hook 不会抛出一个 `Promise`(为了可以和 Suspense 一起使用)。而是会返回一个 `Loadable`,这是一个具有以下接口的对象:

<<<<<<< HEAD
- `state`:表示 selector 的状态。可选的值有 `'hasValue'`,`'hasError'`,`'loading'`。
- `getValue()`:如果有错误,这个函数会抛出这个错误。如果 selector 仍在加载中,它会抛出一个 Promise。否则,它就会返回这个 selector resolve 的值。
- `toPromise()`:返回一个 `Promise`,当 selector resolve 时它也会 resolve。如果该 selector 是异步的或者已经 resolve 了,它就会返回一个立即 resolve 的 `Promise`。

---

- `state`:一个 _可能_ 有一些异步操作的 [`selector`](/docs/api-reference/core/selector) 。给定 selector 的状态决定了返回的 loadable 的状态。
=======
- `state`: indicates the status of the selector. Possible values are `'hasValue'`, `'hasError'`, `'loading'`.
- `contents`: The value represented by this `Loadable`. If the state is `hasValue`, it is the actual value, if the state is `hasError` it is the `Error` object that was thrown, and if the state is `loading`, then it is a `Promise` of the value.
- `getValue()`: if there is an error, this function throws the error. If selector is still loading, it throws a Promise. Otherwise it returns the value that the selector resolved to.
- `toPromise()`: returns a `Promise` that will resolve when the selector has resolved. If the selector is synchronous or has already resolved, it returns a `Promise` that resolves immediately.

---

- `state`: an [`atom`](/docs/api-reference/core/atom) or [`selector`](/docs/api-reference/core/selector) that _may_ have some asynchronous operations. The status of the returned loadable will depend on the status of the provided state selector.
>>>>>>> f2edd6bec9db35bd999e692b046556827f22f44e

### 示例

```jsx
```
function UserInfo({userID}) {
const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
switch (userNameLoadable.state) {
case 'hasValue':
return <div>{userNameLoadable.contents}</div>;
case 'loading':
return <div>Loading...</div>;
case 'hasError':
throw userNameLoadable.contents;
}
}
6 changes: 2 additions & 4 deletions docs/docs/api-reference/utils/selectorFamily.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,9 @@ function selectorFamily<T, Parameter>({
Where

```jsx
type ValueOrUpdater<T> = T | DefaultValue | ((prevValue: T) => T | DefaultValue);
type GetRecoilValue = <T>(RecoilValue<T>) => T;
type SetRecoilValue = <T>(
RecoilState<T>,
T | DefaultValue | ((prevValue: T) => T | DefaultValue),
) => void;
type SetRecoilValue = <T>(RecoilState<T>, ValueOrUpdater<T>) => void;
type ResetRecoilValue = <T>(RecoilState<T>) => void;
```

Expand Down
24 changes: 14 additions & 10 deletions docs/docs/guides/asynchronous-data-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ title: Asynchronous Data Queries
sidebar_label: Asynchronous Data Queries
---

Recoil provides a way to map state and derived state to React components via a data flow graph. What's really powerful is that the functions in the graph can also be asynchronous. This makes it easy to use asynchronous functions in synchronous React component render functions. Recoil allows you to seemlessly mix synchronous and asynchronous functions in your data flow graph of selectors. Simply return a Promise to a value instead of the value itself from a selector `get` callback, the interface remains exactly the same. Because these are just selectors, other selectors can also depend on them to further transform the data.
Recoil provides a way to map state and derived state to React components via a data-flow graph. What's really powerful is that the functions in the graph can also be asynchronous. This makes it easy to use asynchronous functions in synchronous React component render functions. Recoil allows you to seemlessly mix synchronous and asynchronous functions in your data-flow graph of selectors. Simply return a Promise to a value instead of the value itself from a selector `get` callback, the interface remains exactly the same. Because these are just selectors, other selectors can also depend on them to further transform the data.

Selectors can be used as one way to incorporate asynchronous data into the Recoil data-flow graph. Please keep in mind that selectors represent pure functions: For a given set of inputs they should always produce the same results (at least for the lifetime of the application). This is important as selector evaluations may execute one or more times, may be restarted, and may be cached. Because of this, selectors are a good way to model read-only DB queries where repeating a query provides consistent data. If you are looking to synchronize local and server state, then please see [Asynchronous State Sync](asynchronous-state-sync) or [State Persistence](persistence).

## Synchronous Example

For example, here is a simple synchronous atom and selector to get a user name:
For example, here is a simple synchronous [atom](/docs/api-reference/core/atom) and [selector](/docs/api-reference/core/selector) to get a user name:

```jsx
const currentUserIDState = atom({
Expand Down Expand Up @@ -38,7 +40,7 @@ function MyApp() {

## Asynchronous Example

If the user names were stored on some database we need to query, all we need to do is return a `Promise` or use an `async` function. If any dependencies change, the selector will be re-evaluated and execute a new query. The results are cached, so the query will only execute once per unique input.
If the user names were stored in some database we need to query, all we need to do is return a `Promise` or use an `async` function. If any dependencies change, the selector will be re-evaluated and execute a new query. The results are cached, so the query will only execute once per unique input.

```jsx
const currentUserNameQuery = selector({
Expand All @@ -59,7 +61,7 @@ function CurrentUserInfo() {

The interface of the selector is the same, so the component using this selector doesn't need to care if it was backed with synchronous atom state, derived selector state, or asynchronous queries!

But, since React is synchronous, what will it render before the promise resolves? Recoil is designed to work with React Suspense to handle pending data. Wrapping your component with a Suspense boundary will catch any descendents that are still pending and render a fallback UI:
But, since React render functions are synchronous, what will it render before the promise resolves? Recoil is designed to work with [React Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html) to handle pending data. Wrapping your component with a Suspense boundary will catch any descendents that are still pending and render a fallback UI:

```jsx
function MyApp() {
Expand All @@ -75,7 +77,7 @@ function MyApp() {

## Error Handling

But what if the request has an error? Recoil selectors can also throw errors which will then be thrown if a component tries to use that value. This can be caught with a React `<ErrorBoundary>`. For example:
But what if the request has an error? Recoil selectors can also throw errors which will then be thrown if a component tries to use that value. This can be caught with a React [`<ErrorBoundary>`](https://reactjs.org/docs/error-boundaries.html). For example:

```jsx
const currentUserNameQuery = selector({
Expand Down Expand Up @@ -111,7 +113,7 @@ function MyApp() {

## Queries with Parameters

Sometimes you want to be able to query based on parameters that aren't just based on derived state. For example, you may want to query based on the component props. You can do that using the `selectorFamily` helper:
Sometimes you want to be able to query based on parameters that aren't just based on derived state. For example, you may want to query based on the component props. You can do that using the [**`selectorFamily`**](/docs/api-reference/utils/selectorFamily) helper:

```jsx
const userNameQuery = selectorFamily({
Expand Down Expand Up @@ -145,9 +147,11 @@ function MyApp() {
}
```

## Data Flow Graph
## Data-Flow Graph

Remember, by modeling queries as selectors, we can build a data-flow graph mixing state, derived state, and queries! This graph will automatically update and re-render React components as state is updated.

Remember, by modeling queries as selectors, we can build a data flow graph mixing state, derived state, and queries! This graph will automatically update and re-render React components as state is updated.
The following example will render the current user's name and a list of their friends. If a friend's name is clicked on, they will become the current user and the name and list will be automatically updated.

```jsx
const currentUserIDState = atom({
Expand Down Expand Up @@ -217,7 +221,7 @@ function MyApp() {
## Concurrent Requests
If you notice in the above example, the `friendsInfoQuery` uses a query to get the info for each friend. But, by doing this in a loop they are essentially serialized. If the lookup is fast, maybe that's ok. If it's expensive, you can use a concurrency helper such as `waitForAll`, `waitForNone`, or `waitForAny` to run them in parallel. They accept both arrays and named objects of dependencies.
If you notice in the above example, the `friendsInfoQuery` uses a query to get the info for each friend. But, by doing this in a loop they are essentially serialized. If the lookup is fast, maybe that's ok. If it's expensive, you can use a concurrency helper such as [`waitForAll`](/docs/api-reference/utils/waitForAll), [`waitForNone`](/docs/api-reference/utils/waitForNone), or [`waitForAny`](/docs/api-reference/utils/waitForAny) to run them in parallel or handle partial results. They accept both arrays and named objects of dependencies.
```jsx
const friendsInfoQuery = selector({
Expand All @@ -234,7 +238,7 @@ const friendsInfoQuery = selector({
## Without React Suspense
It is not necessary to use React Suspense for handling pending asynchronous selectors. You can also use the `useRecoilValueLoadable()` hook to determine the status during rendering:
It is not necessary to use React Suspense for handling pending asynchronous selectors. You can also use the [`useRecoilValueLoadable()`](/docs/api-reference/core/useRecoilValueLoadable) hook to determine the status during rendering:
```jsx
function UserInfo({userID}) {
Expand Down
186 changes: 186 additions & 0 deletions docs/docs/guides/asynchronous-state-sync.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
---
title: Asynchronous State Sync
sidebar_label: Asynchronous State Sync
---

Recoil [atoms](/docs/api-reference/core/atom) represent local application state. Your application may have remote or server-side state as well, such as via a RESTful API. Consider synchronizing the remote state with Recoil atoms. Doing this allows you to easily access or write to the state from React components using the `useRecoilState()` hook, or use that state as input to the Recoil data-flow graph for other derived state selectors. If you're looking to [query a database or server for read-only data](asynchronous-data-queries), consider using asynchronous selectors.

## Local State Example

This example provides the friend status as local state only.

```jsx
const currentUserIDState = atom({
key: 'CurrentUserID',
default: null,
});

function CurrentUserInfo() {
const [currentUserID] = useRecoilState(currentUserIDState);
return <div>Current User: {currentUserID}</div>;
}
```

## Sync State From Server

We can subscribe to asynchronous changes in the remote state and update the atom value to match. This can be done using standard React [`useEffect()`](https://reactjs.org/docs/hooks-reference.html#useeffect) hook or other popular libraries.

```jsx
function CurrentUserIDSubscription() {
const setCurrentUserID = useSetRecoilState(currentUserIDState);

useEffect(() => {
RemoteStateAPI.subscribeToCurrentUserID(setCurrentUserID);
// Specify how to cleanup after this effect
return function cleanup() {
RemoteServerAPI.unsubscribeFromFriendStatus(setCurrentUserID);
};
}, []);

return null;
}

function MyApp() {
return (
<RecoilRoot>
<CurrentUserIDSubscription />
<CurrentUserInfo />
</RecoilRoot>
);
}
```

If you want to handle synchronization of multiple atoms in a single place, you can also use the [State Persistence](persistence) pattern.

## Bi-Directional Synching

You can also sync the state so local changes are updated on the server:

```jsx
function CurrentUserIDSubscription() {
const [currentUserID, setCurrentUserID] = useRecoilState(currentUserIDState);
const knownServerCurrentUserID = useRef(currentUserID);

// Subscribe server changes to update atom state
useEffect(() => {
function handleUserChange(id) {
knownServerCurrentUserID.current = id;
setCurrentUserID(id);
}

RemoteStateAPI.subscribeToCurrentUserID(handleUserChange);
// Specify how to cleanup after this effect
return function cleanup() {
RemoteServerAPI.unsubscribeFromFriendStatus(handleUserChange);
};
}, [knownServerCurrentUserID]);

// Subscribe atom changes to update server state
useEffect(() => {
if (currentUserID !== knownServerCurrentUserID.current) {
knownServerCurrentID.current = currentUserID;
RemoteServerAPI.updateCurrentUser(currentUserID);
}
}, [currentUserID, knownServerCurrentUserID.current]);

return null;
}
```

## Synching State with Parameters

You can also use the [`atomFamily`](/docs/api-reference/utils/atomFamily) helper to sync local state based on parameters.

```jsx
const friendStatusState = atomFamily({
key: 'Friend Status',
default: 'offline',
});

function useFriendStatusSubscription(id) {
const setStatus = useSetRecoilState(friendStatusState(id));

useEffect(() => {
RemoteStateAPI.subscribeToFriendStatus(id, setStatus);
// Specify how to cleanup after this effect
return function cleanup() {
RemoteServerAPI.unsubscribeFromFriendStatus(id, setStatus);
};
}, []);
}
```

## Data-Flow Graph

An advantage of using atoms to represent remote state is that you can use it as input for other derived state. The following example will show the current user and friend list based on the current server state. If the server changes the current user it will re-render the entire list, if it only changes the status of a friend then only that list entry will be re-rendered. If a list item is clicked on, it will change the current user locally and will update the server state.

```jsx
const userInfoQuery = selectorFamily({
key: 'UserInfoQuery',
get: userID => async ({get}) => {
const response = await myDBQuery({userID});
if (response.error) {
throw response.error;
}
return response;
},
});

const currentUserInfoQuery = selector({
key: 'CurrentUserInfoQuery',
get: ({get}) => get(userInfoQuery(get(currentUserIDState)),
});

const friendColorState = selectorFamily({
key: 'FriendColor',
get: friendID => ({get}) => {
const [status] = useRecoilState(friendStatusState(friendID));
return status === 'offline' ? 'red' : 'green';
}
})

function FriendStatus({friendID}) {
useFriendStatusSubscription(friendID);
const [status] = useRecoilState(friendStatusState(friendID));
const [color] = useRecoilState(friendColorState(friendID));
const [friend] = useRecoilState(userInfoQuery(friendID));
return (
<div style={{color}}>
Name: {friend.name}
Status: {status}
</div>
);
}

function CurrentUserInfo() {
const {name, friendList} = useRecoilValue(currentUserInfoQuery)
const setCurrentUserID = useSetRecoilState(currentUserIDState);
return (
<div>
<h1>{name}</h1>
<ul>
{friendList.map(friendID =>
<li key={friend.id} onClick={() => setCurrentUserID(friend.id)}>
<React.Suspense fallback={<div>Loading...</div>}>
<FriendStatus friendID={friendID} />
</React.Suspense>
</li>
)}
</ul>
</div>
);
}

function MyApp() {
return (
<RecoilRoot>
<ErrorBoundary>
<React.Suspense fallback={<div>Loading...</div>}>
<CurrentUserIDSubscription />
<CurrentUserInfo />
</React.Suspense>
</ErrorBoundary>
</RecoilRoot>
);
}
```
Loading

0 comments on commit fd6b2a7

Please sign in to comment.