react-hoc-query
HOC to help you query restful data. It provides the following convenient features:
- You can query whatever you like, but the primary use case is to query restful API services
- Query results are stored in Redux' store
- so the same query elsewhere will simply fetch from cache (the store) and,
- any updates to the data in store will cause dependent components to refresh and,
- you can use the redux devtool to debug data
- A manual
refetch
is provided via props in case you need to ignore possible cache - Status changes of the query are provided via props (
loading
,error
,data
) - You can optionally setup a global query error handler to centralize error
processing (
query.onError(e)
). E.g, display a dialog for network errors. - You can optionally setup
query.openLoading()
,query.closeLoading()
andquery.loadingWait
to implement displaying a loading indicator UI should the query result delays for too long (longer thanquery.loadingWait
) - You can setup an interval to poll newest data periodically, and control when
to start/end polling via props (
startPolling
,endPolling
) - Multiple queries and query dependencies are supported
- You can create groups to control the overall cache size. Each group has a limit on how much query results it can store, and will discard excessive items using the LRU algorithm.
Installation
yarn add react-hoc-query
Basic Usage
Setup the reducer
const store =
Simple usage
@ from ReactComponent { const loading error data = thispropsquery if loading return <YourLoadingView /> if !data || error return <YourErrorView error=error /> if dataisLoggedIn return <YourHomePage /> else return <YourLoginPage /> }
With the above example, all components with a query key of loginInfo will share the same data. If a cache is available in store, that cache will be used.
Use refetch to guarantee data freshness
@@ from ReactComponent { return <button onClick=thislogin>Login</button> } login = async { try await thisprops await thispropsquery catch e // handle it }
With the above example, loginInfo
is refreshed after successful login, and
every component that depends on loginInfo
will get refreshed with the new
login data.
Use a custom name for prop
@ from ReactComponent { const loading error data = thispropsloginInfo if loading return <YourLoadingView /> if !data || error return <YourErrorView error=error /> if dataisLoggedIn return <YourHomePage /> else return <YourLoginPage /> }
Polling interval
@ from ReactComponent { const data = thispropsquery if data return <YourView data=data /> } { thispropsquery } { thispropsquery }
Query dependencies
In the below example, 3 queries are used by App, but only loginInfo
and
userProfile
are necessary for start displaying content to the users.
loginInfo
and chatChannels
will be fetched concurrently while
userProfile
is only fetched after loginInfo
is available and that the user
is logged in.
Each time one of the dependencies changes, the query gets refetched upon
dependency availability given shouldFetch
returns true.
Also, note that the order in which you apply the queries is important,
queries with dependOn
should appear below their dependencies.
dependOn
is an array of both string and objects of shape { group, key }
.
When an item is a string, it indicates a dependency for the specified key of
the same group as the query itself; otherwise, a { group, key }
is needed to
depend on a key of a different group. Please look further down for the
introduction of groups.
@@@@Component { const loading loginInfo chatChannels userProfile = thisprops if loading return <div>Loading</div> else if userProfiledata return <div className="demo-app">Hello userProfiledataname</div> else return null }
Global onError handler
on your app entry:
query { if errtype === "your_error_type" // dispatch action to open error dialog }
Global loading indicator popup handler
on your app entry:
// If a query finishes before this times out then `openLoading`// will not get called.queryloadingWait = 01 // defaults to 0.1sec query { // dispatch action to popup loading indicator} query { // dispatch action to close loading indicator}
Use group to limit cache size
on your app entry:
// This defines the cache size (number of query results) for the group// `movieItems`. Default size for new groups is 10. Everything else goes// into the `DEFAULT` group, which is practically unlimited.groupsmovieItems = 20
usage:
@ from ReactComponent { const loading error data = thispropsquery // ... }
With the above setup, no matter how many movie items you visit, only the most recent 20 will be cached. This prevents possible overuse of memory.
Dev setup
yarn
npm run tasks
- yarn build: Transpile source code