Current section
Files
Jump to
Current section
Files
src/sprocket@hooks.erl
-module(sprocket@hooks).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/sprocket/hooks.gleam").
-export([callback/4, client/4, dep/1, effect/4, memo/4, provider/3, provide/3, reducer/4, state/3]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/sprocket/hooks.gleam", 64).
-spec maybe_trigger_update(
list(gleam@dynamic:dynamic_()),
gleam@option:option(list(gleam@dynamic:dynamic_())),
GKC,
fun(() -> GKC)
) -> {GKC, gleam@option:option(list(gleam@dynamic:dynamic_()))}.
maybe_trigger_update(Deps, Prev, Value, Updater) ->
case Prev of
{some, Prev_deps} ->
case sprocket@internal@context:compare_deps(Prev_deps, Deps) of
{changed, New_deps} ->
{Updater(), {some, New_deps}};
unchanged ->
{Value, Prev}
end;
none ->
{Updater(), {some, Deps}}
end.
-file("src/sprocket/hooks.gleam", 34).
?DOC(
" Callback Hook\n"
" -------------\n"
" Creates a callback hook that will return a cached version of a given callback\n"
" function and only recompute the callback when specified dependencies change.\n"
" \n"
" This hook is useful for when a component needs to use a callback function that\n"
" is referenced as a dependency by another hook, such as an effect hook.\n"
).
-spec callback(
sprocket@internal@context:context(),
fun(() -> nil),
list(gleam@dynamic:dynamic_()),
fun((sprocket@internal@context:context(), fun(() -> nil)) -> {GKA,
sprocket@internal@context:element()})
) -> {GKA, sprocket@internal@context:element()}.
callback(Ctx, Callback_fn, Deps, Cb) ->
{Ctx@2, Id@1, Current_callback_fn@1, Prev@1, Index@1} = case sprocket@internal@context:fetch_or_init_hook(
Ctx,
fun() ->
{callback,
sprocket@internal@utils@unique:cuid(erlang:element(9, Ctx)),
Callback_fn,
none}
end
) of
{Ctx@1, {callback, Id, Current_callback_fn, Prev}, Index} -> {
Ctx@1,
Id,
Current_callback_fn,
Prev,
Index};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sprocket/hooks"/utf8>>,
function => <<"callback"/utf8>>,
line => 40,
value => _assert_fail,
start => 1435,
'end' => 1617,
pattern_start => 1446,
pattern_end => 1500})
end,
{Callback_fn@1, Deps@1} = maybe_trigger_update(
Deps,
begin
_pipe = Prev@1,
gleam@option:then(
_pipe,
fun(Prev@2) -> erlang:element(2, Prev@2) end
)
end,
Current_callback_fn@1,
fun() -> Callback_fn end
),
Ctx@3 = sprocket@internal@context:update_hook(
Ctx@2,
{callback, Id@1, Callback_fn@1, {some, {callback_result, Deps@1}}},
Index@1
),
Cb(Ctx@3, Callback_fn@1).
-file("src/sprocket/hooks.gleam", 88).
?DOC(
" Client Hook\n"
" -----------\n"
" Creates a client hook that can be used to facilitate communication with a client\n"
" (such as a web browser). The client hook functionality is defined by the client\n"
" and is typically used to send or receive messages to/from the client.\n"
).
-spec client(
sprocket@internal@context:context(),
binary(),
gleam@option:option(fun((binary(), gleam@dynamic:dynamic_(), fun((binary(), gleam@option:option(gleam@dynamic:dynamic_())) -> nil)) -> nil)),
fun((sprocket@internal@context:context(), fun(() -> sprocket@internal@context:attribute()), fun((binary(), gleam@option:option(gleam@dynamic:dynamic_())) -> nil)) -> {sprocket@internal@context:context(),
sprocket@internal@context:element()})
) -> {sprocket@internal@context:context(), sprocket@internal@context:element()}.
client(Ctx, Name, Handle_event, Cb) ->
Init = fun() ->
{client,
sprocket@internal@utils@unique:cuid(erlang:element(9, Ctx)),
Name,
Handle_event}
end,
{Ctx@2, Id@1, Index@1} = case sprocket@internal@context:fetch_or_init_hook(
Ctx,
Init
) of
{Ctx@1, {client, Id, _, _}, Index} -> {Ctx@1, Id, Index};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sprocket/hooks"/utf8>>,
function => <<"client"/utf8>>,
line => 99,
value => _assert_fail,
start => 3034,
'end' => 3136,
pattern_start => 3045,
pattern_end => 3092})
end,
Ctx@3 = sprocket@internal@context:update_hook(
Ctx@2,
{client, Id@1, Name, Handle_event},
Index@1
),
Bind_hook_attr = fun() -> {client_hook, Id@1, Name} end,
Dispatch_event = fun(Kind, Payload) ->
sprocket@internal@context:dispatch_client_hook_event(
Ctx@3,
Id@1,
Kind,
Payload
)
end,
Cb(Ctx@3, Bind_hook_attr, Dispatch_event).
-file("src/sprocket/hooks.gleam", 116).
?DOC(" Creates a hook dependency from some value\n").
-spec dep(any()) -> gleam@dynamic:dynamic_().
dep(Dependency) ->
gleam_stdlib:identity(Dependency).
-file("src/sprocket/hooks.gleam", 125).
?DOC(
" Effect Hook\n"
" -----------\n"
" Creates an effect hook that will run the given effect function when the deps change. The effect\n"
" function is memoized and recomputed when the deps change. The effect function can return a cleanup\n"
" function that will be called when the effect is removed.\n"
).
-spec effect(
sprocket@internal@context:context(),
fun(() -> gleam@option:option(fun(() -> nil))),
list(gleam@dynamic:dynamic_()),
fun((sprocket@internal@context:context()) -> {sprocket@internal@context:context(),
sprocket@internal@context:element()})
) -> {sprocket@internal@context:context(), sprocket@internal@context:element()}.
effect(Ctx, Effect_fn, Deps, Cb) ->
Init = fun() ->
{effect,
sprocket@internal@utils@unique:cuid(erlang:element(9, Ctx)),
Effect_fn,
Deps,
none}
end,
{Ctx@2, Id@1, Prev@1, Index@1} = case sprocket@internal@context:fetch_or_init_hook(
Ctx,
Init
) of
{Ctx@1, {effect, Id, _, _, Prev}, Index} -> {Ctx@1, Id, Prev, Index};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sprocket/hooks"/utf8>>,
function => <<"effect"/utf8>>,
line => 137,
value => _assert_fail,
start => 4376,
'end' => 4481,
pattern_start => 4387,
pattern_end => 4437})
end,
Ctx@3 = sprocket@internal@context:update_hook(
Ctx@2,
{effect, Id@1, Effect_fn, Deps, Prev@1},
Index@1
),
Cb(Ctx@3).
-file("src/sprocket/hooks.gleam", 154).
?DOC(
" Memo Hook\n"
" ---------\n"
" Creates a memo hook that can be used to memoize the result of a function. The memo\n"
" hook will return the result of the function and will only recompute the result when\n"
" the dependencies change.\n"
" \n"
" This hook is useful for optimizing performance by memoizing the result of an\n"
" expensive function between renders.\n"
).
-spec memo(
sprocket@internal@context:context(),
fun(() -> GKG),
list(gleam@dynamic:dynamic_()),
fun((sprocket@internal@context:context(), GKG) -> {sprocket@internal@context:context(),
sprocket@internal@context:element()})
) -> {sprocket@internal@context:context(), sprocket@internal@context:element()}.
memo(Ctx, Memo_fn, Deps, Cb) ->
{Ctx@2, Id@1, Current_memoized@1, Prev@1, Index@1} = case sprocket@internal@context:fetch_or_init_hook(
Ctx,
fun() ->
{memo,
sprocket@internal@utils@unique:cuid(erlang:element(9, Ctx)),
gleam_stdlib:identity(Memo_fn()),
none}
end
) of
{Ctx@1, {memo, Id, Current_memoized, Prev}, Index} -> {
Ctx@1,
Id,
Current_memoized,
Prev,
Index};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sprocket/hooks"/utf8>>,
function => <<"memo"/utf8>>,
line => 160,
value => _assert_fail,
start => 5149,
'end' => 5348,
pattern_start => 5160,
pattern_end => 5215})
end,
{Memoized, Deps@1} = maybe_trigger_update(
Deps,
begin
_pipe = Prev@1,
gleam@option:then(
_pipe,
fun(Prev@2) -> erlang:element(2, Prev@2) end
)
end,
Current_memoized@1,
fun() -> gleam_stdlib:identity(Memo_fn()) end
),
Ctx@3 = sprocket@internal@context:update_hook(
Ctx@2,
{memo, Id@1, Memoized, {some, {memo_result, Deps@1}}},
Index@1
),
Cb(Ctx@3, sprocket@internal@utils@unsafe_coerce:unsafe_coerce(Memoized)).
-file("src/sprocket/hooks.gleam", 195).
?DOC(
" Provider Hook\n"
" ------------\n"
" Creates a provider hook that allows a component to access data from a parent or ancestor component.\n"
" The provider hook will return an optional containing the current value provided from an ancestor using\n"
" a given provider function. If the provider hook is called from a context that does not have a matching\n"
" provider, the hook will return `None`.\n"
" \n"
" The ancestor provides the value by using a custom provider that is unique to the provider hook. This\n"
" custom provider is created using `provider` function in the `sprocket/internal/context` module.\n"
" \n"
" This hook is conceptually similar to the `useContext` hook in React.\n"
).
-spec provider(
sprocket@internal@context:context(),
binary(),
fun((sprocket@internal@context:context(), gleam@option:option(any())) -> {sprocket@internal@context:context(),
sprocket@internal@context:element()})
) -> {sprocket@internal@context:context(), sprocket@internal@context:element()}.
provider(Ctx, Key, Cb) ->
Value = begin
_pipe = erlang:element(10, Ctx),
_pipe@1 = gleam_stdlib:map_get(_pipe, Key),
_pipe@2 = gleam@option:from_result(_pipe@1),
gleam@option:map(
_pipe@2,
fun(V) -> sprocket@internal@utils@unsafe_coerce:unsafe_coerce(V) end
)
end,
Cb(Ctx, Value).
-file("src/sprocket/hooks.gleam", 210).
?DOC(" Creates a new provider element with the given key and value.\n").
-spec provide(binary(), any(), sprocket@internal@context:element()) -> sprocket@internal@context:element().
provide(Key, Value, Element) ->
{provider, Key, gleam_stdlib:identity(Value), Element}.
-file("src/sprocket/hooks.gleam", 220).
?DOC(
" Reducer Hook\n"
" ------------\n"
" Creates a reducer hook that can be used to manage state. The reducer hook will\n"
" return the current state of the reducer and a dispatch function that can be used\n"
" to update the reducer's state. Dispatching a message to the reducer will result\n"
" in a re-render of the component.\n"
).
-spec reducer(
sprocket@internal@context:context(),
fun((fun((GKL) -> nil)) -> GKK),
fun((GKK, GKL, fun((GKL) -> nil)) -> GKK),
fun((sprocket@internal@context:context(), GKK, fun((GKL) -> nil)) -> {sprocket@internal@context:context(),
sprocket@internal@context:element()})
) -> {sprocket@internal@context:context(), sprocket@internal@context:element()}.
reducer(Ctx, Initialize, Update, Cb) ->
{context, _, _, _, _, Trigger_reconciliation, _, _, _, _} = Ctx,
Reducer_init = fun() ->
Reducer_actor@1 = case begin
_pipe = sprocket@internal@reducer:start(
Initialize,
Update,
fun(_) -> Trigger_reconciliation() end
),
gleam@result:map_error(
_pipe,
fun(Error) ->
sprocket@internal@logger:error(
<<"hooks.reducer: failed to start reducer actor"/utf8>>
),
Error
end
)
end of
{ok, Reducer_actor} -> Reducer_actor;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sprocket/hooks"/utf8>>,
function => <<"reducer"/utf8>>,
line => 231,
value => _assert_fail,
start => 7591,
'end' => 7827,
pattern_start => 7602,
pattern_end => 7619})
end,
{reducer,
sprocket@internal@utils@unique:cuid(erlang:element(9, Ctx)),
gleam_stdlib:identity(Reducer_actor@1),
fun() -> sprocket@internal@reducer:shutdown(Reducer_actor@1) end}
end,
{Ctx@2, Dyn_reducer_actor@1} = case sprocket@internal@context:fetch_or_init_hook(
Ctx,
Reducer_init
) of
{Ctx@1, {reducer, _, Dyn_reducer_actor, _}, _} -> {
Ctx@1,
Dyn_reducer_actor};
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sprocket/hooks"/utf8>>,
function => <<"reducer"/utf8>>,
line => 245,
value => _assert_fail@1,
start => 7983,
'end' => 8111,
pattern_start => 7994,
pattern_end => 8059})
end,
Reducer_actor@2 = sprocket@internal@utils@unsafe_coerce:unsafe_coerce(
Dyn_reducer_actor@1
),
Model = sprocket@internal@reducer:get_model(Reducer_actor@2),
Cb(
Ctx@2,
Model,
fun(_capture) ->
sprocket@internal@reducer:dispatch(Reducer_actor@2, _capture)
end
).
-file("src/sprocket/hooks.gleam", 263).
?DOC(
" State Hook\n"
" ----------\n"
" Creates a state hook that can be used to manage state. The state hook will return\n"
" the current state and a setter function that can be used to update the state. Setting\n"
" the state will result in a re-render of the component.\n"
).
-spec state(
sprocket@internal@context:context(),
GKQ,
fun((sprocket@internal@context:context(), GKQ, fun((GKQ) -> nil)) -> {sprocket@internal@context:context(),
sprocket@internal@context:element()})
) -> {sprocket@internal@context:context(), sprocket@internal@context:element()}.
state(Ctx, Initial, Cb) ->
{context, _, _, _, _, Trigger_reconciliation, Update_hook, _, _, _} = Ctx,
Init_state = fun() ->
{state,
sprocket@internal@utils@unique:cuid(erlang:element(9, Ctx)),
gleam_stdlib:identity(Initial)}
end,
{Ctx@2, Hook_id@1, Value@1} = case sprocket@internal@context:fetch_or_init_hook(
Ctx,
Init_state
) of
{Ctx@1, {state, Hook_id, Value}, _} -> {Ctx@1, Hook_id, Value};
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sprocket/hooks"/utf8>>,
function => <<"state"/utf8>>,
line => 278,
value => _assert_fail,
start => 9100,
'end' => 9206,
pattern_start => 9111,
pattern_end => 9156})
end,
Setter = fun(Value@2) ->
Update_hook(Hook_id@1, fun(Hook) -> case Hook of
{state, Id, _} when Id =:= Hook_id@1 ->
{state, Id, gleam_stdlib:identity(Value@2)};
_ ->
sprocket@internal@exceptions:throw_on_unexpected_hook_result(
Hook
)
end end),
Trigger_reconciliation()
end,
Cb(
Ctx@2,
sprocket@internal@utils@unsafe_coerce:unsafe_coerce(Value@1),
Setter
).