What is the idiomatic way to handle data fetching in re-frame? I come from a react background and am searching for an equivalent to Redux Toolkit Query in re-frame. Redux data fetching:
export const api = createApi({
reducerPath: 'api',
tagTypes: [ApiTags.Activity],
baseQuery: fetchBaseQuery({
baseUrl: `${API_URL}api/v1`,
}),
endpoints: (builder) => ({
getActivities: builder.query({
query: () => `/activities`,
providesTags: (_queryCache, _error, _params) => [
{ type: ApiTags.Activity, id: `LIST` },
],
}),
}),
})
export const {useGetActivitiesQuery} = api
As you can see here, we define an API endpoint, and this is now an API reducer. The API will create specific react hooks, which is a side-effect because when you call this hook, it will issue the request.
function MyComponent(){
const {isLoading, data, isError, isSuccess} = useGetActivitiesQuery()
if (isLoading) {return }
if (isError) {return }
return
}
My initial reaction was to look for something similar in re-frame since redux and re-frame have similar ideas, although different in many parts. Redux does not differentiate between events and effects; it just has actions and thunk actions (async actions - usually with a side effect)
But then I read https://ericnormand.me/article/react-vs-re-frame, and the first one is this exact pattern of fetching data on a lifecycle when a component mounts.
• What is the idiomatic way to fetch data with re-frame?
• Do you fetch all required data at the beginning or attach the effect to a navigation event?Thank you for the responses, guys! It seems like a kee-frame is what I was looking for.
We used https://github.com/ingesolvoll/kee-frame to great success in the last project I was on. Particularly for the route-centric aspects.
Although we also utilised the success/failed pattern of requests with https://github.com/Day8/re-frame-http-fx
(rf/reg-event-fx ::submit-success
(fn [{:keys [db]} [_ {:keys [path]} _response]]
{:db (fork/set-submitting db path false)
:dispatch [...]}))
(rf/reg-event-fx ::submit-failure
(fn [{:keys [db]} [_ {:keys [path]} http-error]]
{:db (fork/set-submitting db path false)
::ui/show-message {:message "Could not save your details"
:http-error http-error}}))
(rf/reg-event-fx ::submit
(fn [{:keys [db]} [_ user-id {:keys [values path] :as fork-params}]]
{:db (fork/set-submitting db path true)
:http-xhrio (ui/build-http
{:method :put
:uri (str "/path/" user-id)
:params (attorney-common/transform-on-submit values)
:on-success [::submit-success fork-params]
:on-failure [::submit-failure fork-params]})}))
Heh, even when I was still using re-frame-http-fx, I renamed :http-xhrio immediately. :)
Agreed! That one is not well named
There's https://github.com/day8/re-frame-http-fx There's also a version that uses fetch if that's more your cup of tea
I'd suggest using https://github.com/superstructor/re-frame-fetch-fx instead.
> What is the idiomatic way to fetch data with re-frame?
Create an event that uses an effect made by either of the two libraries (or create your own, it's trivial with js/fetch), create events that handle success and failure, use the initial event whenever you need to load some data.
> Do you fetch all required data at the beginning or attach the effect to a navigation event?
Depends on the app and use cases.
In my apps, loading all the data that a user might need would keep them waiting for minutes to hours, so I load at navigation or some other explicit action, like expanding some panel.
You may already be familiar, but we also use https://github.com/ingesolvoll/kee-frame on top of re-frame for extra things like hooking route navigation to fx
Then each route has constructs like
(k/reg-controller
:alert
{:params (fn [{{:keys [name]} :data {:keys [mrn]} :path-params :as e}]
(when (= name :alert) mrn))
:start (fn [_ mrn]
(let [logged-in @(rf/subscribe [::subs/logged-in])]
(if logged-in
[::init! mrn]
[::login/login!])))
:stop (fn [_ _]
[::reset!])})
(rf/reg-event-fx
::init!
(fn [{:keys [db]} [_ mrn]]
(debug "init:" mrn)
{:db (assoc-in db [::state :mrn] mrn)
:fx [[:dispatch [::refresh!]]]}))
(rf/reg-event-fx
::refresh!
(fn [{:keys [db]} _]
..
{}))and the “page load” logic goes in the refresh! fx
the nice thing is anything can trigger a refresh (websocket event, component action, etc)