A simple React Hooks toolbox written in ReScript.
All the hooks are documented in code so that the official plugin for VScode can display the documentation most of the time (docs are not displayed for modules and functors unfortunately).
Additionally to the in code documentation you'll find below a short description of what the hooks do and an example for each.
Feel free to explore the example
if you want some "real world" examples.
Calls the provided onClickOutside
function when the user clicks on a dom element that is not whitelisted in the refs
argument.
You can deactivate the on click or the on touch event using the ~onClick=false
or ~onTouch=false
arguments.
Additionally to the use
hook this module comes with 5 other hooks use0
to use4
that serve the same purpose as the official React.useEffectX
hooks.
Example:
let whitelistedRef = React.useRef(Js.Nullable.null)
let (isDisplayed, setIsDisplayed) = React.useState(() => false)
ClickOutsideHook.use0(~refs=[whitelistedRef], () => setIsDisplayed(_ => false))
<div ref={ReactDOM.Ref.domRef(whitelistedRef)}>
{`Div below is displayed: ${isDisplayed ? "true" : "false"}`->React.string}
<div readOnly=true onClick={_ => setIsDisplayed(_ => true)} value=text>
{(isDisplayed ? "I'm displayed now!" : "Click me")->React.string}
</div>
</div>
In order to use this hook you need to call the Make
functor. You only need to do this once for your whole application.
The module passed to the Make
functor must contain a resolve
function that takes the result of the WindowHook
hook and return a value of type t
which must be defined too.
Like the WindowHook
hook which DeviceHook
is based on, it can be used only client side and will always return None
on the server side.
module MyDeviceHook = DeviceHook.Make({
type t = [#unknown | #mobile | #tablet | #desktop]
let resolve = ({WindowHook.innerWidth: innerWidth}) =>
switch () {
| _ if innerWidth >= 375 && innerWidth < 768 => #mobile
| _ if innerWidth >= 768 && innerWidth < 1024 => #tablet
| _ if innerWidth >= 1024 => #desktop
| _ => #unknown
}
})
Alternatively you can use the make
function that is stricly equivalent but can feel more familiar to some developers:
module MyDeviceHook = unpack(
DeviceHook.make(~resolve=({innerWidth}): [#mobile | #tablet | #desktop | #unknown] =>
switch () {
| _ if innerWidth >= 375 && innerWidth < 768 => #mobile
| _ if innerWidth >= 768 && innerWidth < 1024 => #tablet
| _ if innerWidth >= 1024 => #desktop
| _ => #unknown
}
)
)
Now our MyDeviceHook
hook has been defined with our rules, we can use it:
let device = MyDeviceHook.use()
<div>
{switch device {
| None => "Rendered from the server side"
| Some((#mobile | #tablet | #desktop) as device) =>
`Rendered from the client side, device is ${((device :> [#mobile | #tablet | #desktop]) :> string)}`
| Some(#unknown) => "Rendered from the client side, unknown device"
}->React.string}
</div>
Notice that we need the double type casting using :>
only if we intend to use the device type as a string. Also, you can return any type from the resolve
function.
This hook will return the currently selected files
and an imperative openFolder
function.
It also returns the openFolderProps
which must be passed down to the Input
component provided with this hook.
Be aware that the files
object reference will always be the same as long as the user doesn't upload any
new files, and can safely be used in a React.useEffect
dependencies array.
Also, you should use openFolder
only in events or in React.useEffect
hooks.
This hook works only on the client side.
Example:
let {files, openFolder, openFolderProps} = OpenFolderHook.use()
React.useEffect1(() => {
// Handle loaded files
}, [files])
<div>
// The required hidden input
<OpenFolderHook.Input openFolderProps />
// The trigger, it can be any html element.
// You can also call the `openFolder` function
// in a `useEffect` hook
<button onClick={_ => openFolder()}>
{"Click to open the folder"->React.string}
</button>
</div>
All-in-one window hook.
Will return an object with some useful up-to-date data of the window
object.
The hook will always returns a None
value on the backend, if your application works only
on the frontend, you can safely get the data using Belt.Option.getExn
or the safer Belt.Option.getWithDefault
function with an empty window object as default.
Example:
let window = WindowHook.use()
<div>
{switch window {
| None => "Rendered from the server side"->React.string
| Some({innerWidth, innerHeight, outerWidth, outerHeight, scrollX, scrollY}) => ...
}}
</div>
A collection of window listeners hooks.
The API is the same for all the hooks in this module with the use
, use0
, use1
, etc... hook
functions that will take a listener. The name of the hook is similar to the React.useEffectX
hooks.
All the event listeners are disposed when the component is unmounted.
@react.component
let make = () => {
WindowListenersHook.Click.use0(_event => {
Js.log("Click on window")
})
}
Notice that if in the above example the hook expects the listener to returns unit
, some hooks will expect a different return type, like the BeforeUnload
hook for instance.
It's possible to add your own window listener:
module MyEvent = {
// You first need the add/remove event listener for the event you want to listen to.
// Here we assume the event will dispatch a `string` value and expects `unit` from the listener.
@send
external addEventListener: (Webapi.Dom.Window.t, @as("my-event") _, string => unit) => unit =
"addEventListener"
@send
external removeEventListener: (Webapi.Dom.Window.t, @as("my-event") _, string => unit) => unit =
"removeEventListener"
// Let's include all the hooks in the `MyEvent` module
include unpack(
make(
~addEventListener=(listener, window) => window->addEventListener(listener),
~removeEventListener=(listener, window) => window->removeEventListener(listener),
)
)
}
You can now use it in a component:
@react.component
let make = () => {
MyEvent.use0(value => {
Js.log(`Got ${value} from the event emitter`)
})
}