CacheMeIfYouCan.ReactiveCache (CacheMeIfYouCan v0.2.0)
View SourceStruct that defines a cached property configuration.
Important
This module is not intended to be used directly. Structs should be
created via the @reactive_cache module attribute in the consuming LiveView.
The fields documented in this module correspond to the keywords in the @reactive_cache
module attribute.
Fields
:keyUsed to identify this cached property internally. Must match the assigns key that the property value will be stored under.:default_valueThe initial value of the property that will be set on thekeyin the assigns when usingCacheMeIfYouCan.LiveViewCache.assign_new_cached/3:depsList of atoms corresponding to other assigns keys. The value of this property will be recomputed whenever any of these assigns keys change.:cbA 1- or 2-arity function that takes the socket and returns a modified socket. This function is called whenever the value needs to be recomputed and should set the value in the assigns before returning the modified socket.The reactive cache only updates the assigns implicitly when calling
CacheMeIfYouCan.LiveViewCache.assign_new_cached/3. All other updates should happen inside this callback function.Because the callback can make any modifications to the socket, it's possible to create an infinite loop by calling
CacheMeIfYouCan.LiveViewCache.assign_cached/3on a key in thedepslist for the corresponding property. If a dependency needs to be updated within the callback for a property for some reason, the regularPhoenix.Component.assign/3can be used to bypass the recomputation check.The 2-arity version should accept an atom as its second argument, and it will be passed the
key. This allows defining helper functions for common groups of assigns and matching on the assigns key in the function head.Example
@reactive_cache [ key: :user_list, default_value: [], deps: [:sort_order, :page_no, :page_size, :filters], cb: &__MODULE__.refresh_data/1, ] @reactive_cache [ key: :page_count, default_value: 1, deps: [:page_size, :filters], cb: &__MODULE__.refresh_data/2, ] # refresh_data/1 callback will be called with only the socket. def refresh_data(socket) when is_reactive(socket) do %{filters: filters} = socket.assigns %{sort_order: sort_order} = socket.assigns %{page_no: page_no, page_size: page_size} = socket.assigns stream_async(socket, :user_list, fn -> res = from(User) |> filter_user_query(filters) |> order_by([user: u], {^sort_order, :username}) |> limit(^page_size) |> offset((^page_no - 1) * ^page_size) |> Repo.all() {:ok, res, reset: true} end) end # refresh_data/2 callback will be called with the socket and the key. def refresh_data(socket, :page_count) when is_reactive(socket) do %{filters: filters, page_size: page_size} = socket.assigns start_async(socket, :fetch_page_count, fn -> from(User) |> filter_user_query(filters) |> Repo.aggregate(:count) |> (&(&1 / page_size)).() |> Float.ceil() |> trunc() end) end
Summary
Types
@type reactive_callback() :: (reactive_socket() -> reactive_socket()) | (reactive_socket(), atom() -> reactive_socket())
@type t() :: %CacheMeIfYouCan.ReactiveCache{ cb: reactive_callback(), default_value: term(), deps: [atom()], key: atom() }