reason-recoil
Version:

201 lines (149 loc) • 3.98 kB
Markdown
# reason-recoil

> Zero-cost bindings to Facebook's [Recoil](https://recoiljs.org) library
⚠️ These bindings are still in experimental stages, use with caution
## Installation
Run the following command:
```console
$ yarn add recoil reason-recoil
```
Then add `reason-recoil` to your `bsconfig.json`'s dependencies:
```diff
{
"bs-dependencies": [
+ "reason-recoil"
]
}
```
## Usage
### Atom
```reason
let textState = Recoil.atom({
key: "textState",
default: "",
});
```
### Selector
A nice feature the OCaml type-system enables is the ability to differenciate Recoil values between the ones that can **only read state** with the ones that can **write state**. This way, you **can't** use hooks with write capabilities with a read-only value.
#### With read only capabilities
```reason
let textStateSize = Recoil.selector({
key: "textStateSize",
get: ({get}) => {
let textState = get(textState);
Js.String.length(textState);
},
});
```
#### With write capabilities
```reason
let textStateSize = Recoil.selector({
key: "textStateSize",
get: ({get}) => {
let textState = get(textState);
Js.String.length(textState);
},
set: ({set}, newSize) => {
let currentTextState = get(textState);
set(textState, currentTextState->Js.String.slice(~from=0, ~to_=newSize));
}
});
```
#### Async
```reason
let user = Recoil.asyncSelector({
key: "user",
get: ({get}) => {
fetchUser(get(currentUserId))
},
});
```
### Hooks
#### `useRecoilState`
```reason
let (state, setState) = Recoil.useRecoilState(textState);
state; // read
setState(textState => newTextState); // write
```
#### `useRecoilValue`
```reason
let state = Recoil.useRecoilValue(textState);
state; // read
```
#### `useSetRecoilState`
```reason
let setState = Recoil.useSetRecoilState(textState);
setState(textState => newTextState); // write
```
#### `useResetRecoilState`
```reason
let reset = Recoil.useResetRecoilState(textState);
reset(); // write
```
#### `useRecoilCallback`
- Dependency free (reevaluates the callback at every render): `useRecoilCallback(...)`
- Zero-dependency (never reevaluates the callback): `useRecoilCallback0(...)`
- One-dependency (reevaluates when dep changes): `useRecoilCallback1(..., [|dep|])`
- Multiple-dependencies: `useRecoilCallback2(..., (dep1, dep2))` (goes from 2 to 5)
```reason
let onClick = Recoil.useRecoilCallback(({snapshot: {getPromise}}, event) => {
let _ = getPromise(myAtom)
|> Js.Promise.then_(value => {
Js.log(value);
Js.Promise.resolve()
});
});
<button onClick={onClick}>
"Click me"->React.string
</button>
```
#### `useRecoilValueLoadable`
```reason
let loadable = Recoil.useRecoilValueLoadable(textState);
Js.log(loadable->Recoil.Loadable.state);
Js.log(loadable->Recoil.Loadable.getValue);
```
## Examples
The [Recoil Basic Tutorial](https://recoiljs.org/docs/basic-tutorial/intro) has been made in ReasonReact: [check the source](./examples/TodoList.re)!
You can run it using:
```console
$ yarn examples
```
and going to [http://localhost:8000/TodoList.html](http://localhost:8000/TodoList.html)
### Memoization
```reason
type t = {
id: string,
value: string,
isCompleted: bool,
};
// For atoms
let todoItemFamily = Recoil.atomFamily({
key: "todo",
default: param => {
id: param,
value: "",
isCompleted: false,
},
});
// For selectors
let todoItemLengthFamily = Recoil.selectorFamily({
key: "todo",
// The `Fn` wrapper is needed here so that BuckleScript
// outputs the correct value
get: param => Fn(({get}) => {
get(todoItemFamily(param)).value->Js.String2.length;
}),
});
```
And use it within a React component:
```reason
[@react.component]
let make = (~todoId) => {
let (todo, setTodo) = Recoil.useRecoilState(todoItemFamily(id));
// ...
<>
{todo.value->React.string}
</>
};
```