Complete React hooks implementation providing state management, side effects, memoization, and lifecycle hooks with full type safety and dependency array support.
[@bs.module "react"]
external useState: ([@bs.uncurry] (unit => 'state)) => ('state, ('state => 'state) => unit) = "useState";The useState hook requires a lazy initializer function to handle any type of state safely.
Usage Example:
[@react.component]
let make = () => {
let (count, setCount) = React.useState(() => 0);
let (name, setName) = React.useState(() => "");
<div>
<p> {React.string("Count: " ++ string_of_int(count))} </p>
<button onClick={_ => setCount(c => c + 1)}>
{React.string("Increment")}
</button>
</div>
}[@bs.module "react"]
external useReducer: ([@bs.uncurry] (('state, 'action) => 'state), 'state) => ('state, 'action => unit) = "useReducer";
[@bs.module "react"]
external useReducerWithMapState: (
[@bs.uncurry] (('state, 'action) => 'state),
'initialState,
[@bs.uncurry] ('initialState => 'state)
) => ('state, 'action => unit) = "useReducer";Usage Example:
type action =
| Increment
| Decrement
| Reset;
type state = {count: int};
let reducer = (state, action) => {
switch (action) {
| Increment => {count: state.count + 1}
| Decrement => {count: state.count - 1}
| Reset => {count: 0}
}
};
[@react.component]
let make = () => {
let (state, dispatch) = React.useReducer(reducer, {count: 0});
<div>
<p> {React.string("Count: " ++ string_of_int(state.count))} </p>
<button onClick={_ => dispatch(Increment)}>
{React.string("+")}
</button>
<button onClick={_ => dispatch(Decrement)}>
{React.string("-")}
</button>
<button onClick={_ => dispatch(Reset)}>
{React.string("Reset")}
</button>
</div>
}/* Run on every render */
[@bs.module "react"]
external useEffect: ([@bs.uncurry] (unit => option(unit => unit))) => unit = "useEffect";
/* Run once on mount */
[@bs.module "react"]
external useEffect0: ([@bs.uncurry] (unit => option(unit => unit)), [@bs.as {json|[]|json}] _) => unit = "useEffect";
/* Run when dependencies change */
[@bs.module "react"]
external useEffect1: ([@bs.uncurry] (unit => option(unit => unit)), array('a)) => unit = "useEffect";
[@bs.module "react"]
external useEffect2: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b)) => unit = "useEffect";
[@bs.module "react"]
external useEffect3: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c)) => unit = "useEffect";
[@bs.module "react"]
external useEffect4: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd)) => unit = "useEffect";
[@bs.module "react"]
external useEffect5: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd, 'e)) => unit = "useEffect";
[@bs.module "react"]
external useEffect6: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd, 'e, 'f)) => unit = "useEffect";
[@bs.module "react"]
external useEffect7: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = "useEffect";Usage Examples:
[@react.component]
let make = (~userId: int) => {
let (user, setUser) = React.useState(() => None);
/* Effect with cleanup */
React.useEffect1(() => {
let timer = setInterval(() => {
fetchUser(userId, setUser);
}, 5000);
/* Cleanup function */
Some(() => clearInterval(timer))
}, [|userId|]);
/* Effect that runs once */
React.useEffect0(() => {
setupAnalytics();
None /* No cleanup needed */
}, ());
/* Multiple dependencies */
React.useEffect2(() => {
updateTitle(user, userId);
None
}, (user, userId));
<div>
{switch (user) {
| Some(u) => <UserProfile user=u />
| None => React.string("Loading...")
}}
</div>
}[@bs.module "react"]
external useLayoutEffect: ([@bs.uncurry] (unit => option(unit => unit))) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect0: ([@bs.uncurry] (unit => option(unit => unit)), [@bs.as {json|[]|json}] _) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect1: ([@bs.uncurry] (unit => option(unit => unit)), array('a)) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect2: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b)) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect3: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c)) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect4: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd)) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect5: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd, 'e)) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect6: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd, 'e, 'f)) => unit = "useLayoutEffect";
[@bs.module "react"]
external useLayoutEffect7: ([@bs.uncurry] (unit => option(unit => unit)), ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => unit = "useLayoutEffect";[@bs.module "react"]
external useMemo: ([@bs.uncurry] (unit => 'any)) => 'any = "useMemo";
[@bs.module "react"]
external useMemo0: ([@bs.uncurry] (unit => 'any), [@bs.as {json|[]|json}] _) => 'any = "useMemo";
[@bs.module "react"]
external useMemo1: ([@bs.uncurry] (unit => 'any), array('a)) => 'any = "useMemo";
[@bs.module "react"]
external useMemo2: ([@bs.uncurry] (unit => 'any), ('a, 'b)) => 'any = "useMemo";
[@bs.module "react"]
external useMemo3: ([@bs.uncurry] (unit => 'any), ('a, 'b, 'c)) => 'any = "useMemo";
[@bs.module "react"]
external useMemo4: ([@bs.uncurry] (unit => 'any), ('a, 'b, 'c, 'd)) => 'any = "useMemo";
[@bs.module "react"]
external useMemo5: ([@bs.uncurry] (unit => 'any), ('a, 'b, 'c, 'd, 'e)) => 'any = "useMemo";
[@bs.module "react"]
external useMemo6: ([@bs.uncurry] (unit => 'any), ('a, 'b, 'c, 'd, 'e, 'f)) => 'any = "useMemo";
[@bs.module "react"]
external useMemo7: ([@bs.uncurry] (unit => 'any), ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'any = "useMemo";Usage Example:
[@react.component]
let make = (~items: array(item), ~filter: string) => {
let filteredItems = React.useMemo2(() => {
items->Array.keep(item => String.includes(item.name, filter))
}, (items, filter));
let itemCount = React.useMemo1(() => {
Array.length(filteredItems)
}, [|filteredItems|]);
<div>
<p> {React.string("Found " ++ string_of_int(itemCount) ++ " items")} </p>
{filteredItems->Array.map(item =>
<ItemCard key=item.id item />
)->React.array}
</div>
}type callback('input, 'output) = 'input => 'output;
[@bs.module "react"]
external useCallback: ([@bs.uncurry] ('input => 'output)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback0: ([@bs.uncurry] ('input => 'output), [@bs.as {json|[]|json}] _) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback1: ([@bs.uncurry] ('input => 'output), array('a)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback2: ([@bs.uncurry] ('input => 'output), ('a, 'b)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback3: ([@bs.uncurry] ('input => 'output), ('a, 'b, 'c)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback4: ([@bs.uncurry] ('input => 'output), ('a, 'b, 'c, 'd)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback5: ([@bs.uncurry] ('input => 'output), ('a, 'b, 'c, 'd, 'e)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback6: ([@bs.uncurry] ('input => 'output), ('a, 'b, 'c, 'd, 'e, 'f)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback7: ([@bs.uncurry] ('input => 'output), ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => callback('input, 'output) = "useCallback";Usage Example:
[@react.component]
let make = (~onItemClick: item => unit, ~selectedId: option(int)) => {
let handleClick = React.useCallback2((item) => {
onItemClick(item);
}, (onItemClick, selectedId));
let handleKeyPress = React.useCallback1((event) => {
if (ReactEvent.Keyboard.key(event) === "Enter") {
handleClick(currentItem);
}
}, [|handleClick|]);
<div onClick=handleClick onKeyPress=handleKeyPress>
{React.string("Click me")}
</div>
}[@bs.module "react"]
external useImperativeHandle0: (
Js.Nullable.t(ref('value)),
[@bs.uncurry] (unit => 'value),
[@bs.as {json|[]|json}] _
) => unit = "useImperativeHandle";
[@bs.module "react"]
external useImperativeHandle1: (Js.Nullable.t(ref('value)), [@bs.uncurry] (unit => 'value), array('a)) => unit = "useImperativeHandle";
[@bs.module "react"]
external useImperativeHandle2: (Js.Nullable.t(ref('value)), [@bs.uncurry] (unit => 'value), ('a, 'b)) => unit = "useImperativeHandle";
[@bs.module "react"]
external useImperativeHandle3: (
Js.Nullable.t(ref('value)),
[@bs.uncurry] (unit => 'value),
('a, 'b, 'c)
) => unit = "useImperativeHandle";
[@bs.module "react"]
external useImperativeHandle4: (
Js.Nullable.t(ref('value)),
[@bs.uncurry] (unit => 'value),
('a, 'b, 'c, 'd)
) => unit = "useImperativeHandle";
[@bs.module "react"]
external useImperativeHandle5: (
Js.Nullable.t(ref('value)),
[@bs.uncurry] (unit => 'value),
('a, 'b, 'c, 'd, 'e)
) => unit = "useImperativeHandle";
[@bs.module "react"]
external useImperativeHandle6: (
Js.Nullable.t(ref('value)),
[@bs.uncurry] (unit => 'value),
('a, 'b, 'c, 'd, 'e, 'f)
) => unit = "useImperativeHandle";
[@bs.module "react"]
external useImperativeHandle7: (
Js.Nullable.t(ref('value)),
[@bs.uncurry] (unit => 'value),
('a, 'b, 'c, 'd, 'e, 'f, 'g)
) => unit = "useImperativeHandle";Alternative hooks with uncurried function signatures for performance optimization:
module Uncurried = {
type callback('input, 'output) = (. 'input) => 'output;
[@bs.module "react"]
external useState: ([@bs.uncurry] (unit => 'state)) => ('state, (. ('state => 'state)) => unit) = "useState";
[@bs.module "react"]
external useReducer: ([@bs.uncurry] (('state, 'action) => 'state), 'state) => ('state, (. 'action) => unit) = "useReducer";
[@bs.module "react"]
external useReducerWithMapState: (
[@bs.uncurry] (('state, 'action) => 'state),
'initialState,
[@bs.uncurry] ('initialState => 'state)
) => ('state, (. 'action) => unit) = "useReducer";
[@bs.module "react"]
external useCallback: ([@bs.uncurry] ('input => 'output)) => callback('input, 'output) = "useCallback";
[@bs.module "react"]
external useCallback0: ([@bs.uncurry] ('input => 'output), [@bs.as {json|[]|json}] _) => callback('input, 'output) = "useCallback";
/* ... useCallback1 through useCallback7 with same pattern */
};let useCounter = (~initialValue=0, ()) => {
let (count, setCount) = React.useState(() => initialValue);
let increment = React.useCallback0(() => {
setCount(c => c + 1);
}, ());
let decrement = React.useCallback0(() => {
setCount(c => c - 1);
}, ());
let reset = React.useCallback1(() => {
setCount(_ => initialValue);
}, [|initialValue|]);
(count, increment, decrement, reset);
};
/* Usage */
[@react.component]
let make = () => {
let (count, increment, decrement, reset) = useCounter(~initialValue=10, ());
<div>
<p> {React.string(string_of_int(count))} </p>
<button onClick={_ => increment()}> {React.string("+")} </button>
<button onClick={_ => decrement()}> {React.string("-")} </button>
<button onClick={_ => reset()}> {React.string("Reset")} </button>
</div>
}type appState = {
user: option(user),
isLoading: bool,
error: option(string),
theme: string,
};
type appAction =
| SetUser(option(user))
| SetLoading(bool)
| SetError(option(string))
| SetTheme(string);
let appReducer = (state, action) => {
switch (action) {
| SetUser(user) => {...state, user, isLoading: false}
| SetLoading(isLoading) => {...state, isLoading}
| SetError(error) => {...state, error, isLoading: false}
| SetTheme(theme) => {...state, theme}
}
};
[@react.component]
let make = () => {
let (state, dispatch) = React.useReducer(appReducer, {
user: None,
isLoading: false,
error: None,
theme: "light"
});
React.useEffect0(() => {
dispatch(SetLoading(true));
fetchCurrentUser()
|> Promise.then_(user => {
dispatch(SetUser(Some(user)));
Promise.resolve();
})
|> Promise.catch(error => {
dispatch(SetError(Some("Failed to load user")));
Promise.resolve();
})
|> ignore;
None;
}, ());
<div className={state.theme}>
{switch (state.user, state.isLoading, state.error) {
| (Some(user), false, None) => <UserDashboard user />
| (None, true, None) => React.string("Loading...")
| (None, false, Some(error)) => <ErrorMessage error />
| _ => React.string("Something went wrong")
}}
</div>
}