redux-request-tracker
About redux-request-tracker
What is redux-request-tracker?
redux-request-tracker
is a package that aims to make working with async data and pagination (link headers support only) a breeze.
Why redux-request-tracker?
So there are number of other packages / patterns out there for tracking async state in your redux applications. here is a good artical for how redux recommends tacking async state. Note that the state is mixed in alongside the data it is related to.
postsBySubreddit: frontend: isFetching: true didInvalidate: false items: reactjs: isFetching: false didInvalidate: false lastUpdated: 1439478405547 items: 42 100
This is one vaild approach for handling async data, but it comes with tradeoffs.
- Boilerplate: There is a decent amount of boilerplate that needs to go into your actions and reducers to handle all of the async logic,
INVALIDATE_SOMETHING
,REQUEST_SOMETHING
,RECEIVE_SOMETHING
. - Mixed-in metadata: With this approach each reducer is responsible for updating & tracking it's own async data.
redux-request-tracker
takes a different approach. All async actions pass through middleware which is responsible for setting up all the request, response, and failure logic. Then using higher order view components and data accessors, you're able to tap into request state managed by the middleware. There are some nice side effects that come along with organizing your code like this.
- All async logic is separated for raw data
Okay so what does that really mean? Let's say you have a
posts
collection. This collection no longer needs to care about whether or not it's loading so it can just focus on the state of posts. When your UI needs to respond to async events, likeisLoading
, you query your request store using the provided data accessors & or use the provided HO component(s). - All async data is collocated
What does that buy you? Glad you asked :) so now you can easily perform queries on all you async state to determine;
- "how often do I make this request?",
- "what routes call which endpoints",
- "how often does this request get called?",
- and so on.
- Request state history
I just alluded to this a bit in the previous point, but the way
redux-request-tracker
stores its data, it accumulates requests in working memory for all async events that have been triggered for the life of the session. This makes it easy to answer some of the questions above.
Getting Started
Install
npm install -S redux-request-tracker
Setup
There are essentially two required steps to integrate redux-request-tracker
into your redux backed application.
Step One (add middleware)
To add redux-request-tracker
middleware to your project, import the middleware module and combine it with your other redux middleware.
NOTE: redux-request-tracker
does have a dependency on redux-thunk
to work properly.
const loggerMiddleware = { return }
Step Two (add reducer)
To add the redux-request-tracker
reducer to your project, import the reducer module and combine it with your other reducers.
todos counter request // <- ADD REDUCER | NOTE! As of right now, you must name this reducer "request".
redux-request-tracker
Using After adding the reducer and middleware you're all set and ready to begin using redux-request-tracker
. All that is required now is to write / modify your actions so they get picked up by your request tracking middleware.
const getTodos = { return { ; };};
In this example were using the fetch api
, but as long as the dispatched action matches the redux-request-tracker
middleware signature, you can use any xhr library.
Middleware signature
For your requests to be picked up by the middleware layer, all you need to do is attach a promise to the request
property.
The following are the currently supported request action options.
- type: String (required) unique namespace for the action
- request: Promise (required)
- onSuccess: Function (optional) callback that receives the request body
- onFailure: Function (optional) callback that receives the response error on failure
- complete: Function (optional) callback that is triggered after success or failure
Middleware options
The redux-request-tracker
middleware takes the following options
- onUnauthorized: Function (request) => any | optional | callback triggered on all 401 responses
- getRequestMethod: Function (request) => string | optional | override for getting the request method from your request object, may differ from request lib to request lib.
- getRequestUrl: Function (request) => string | optional | override for getting the request url from your request object, may differ from request lib to request lib.
Connecting to your components
As a convience, redux-request-tracker
exposes connectRequest
, a higher order function that binds request metadata to your components.
connectRequest
Connect request can take the following parameters in an options hash
- requestName string | function (props) => string
;; { return // ... } requestName: 'REQUEST_GET_TODOS' MyTodoComponent;
Dynamic request tracking
When connecting a component to your request store, you may not have a hard coded action that maps to a specific request. Lets say, for example, that you have a component that lists out a collection of TODOS. You could create the constant REQUEST_FETCH_TODOS
in your actions that you would pass to the list views connectRequest
function. Now lets say you have a component that only displays a single request and you have an action responsible for fetching individual todos. In this case you'll need a way to dynamically namespace your requests for each todo. To accomplish this, connectRequest
, allows you to define the requestName
option as a function that receives props
as an argument. The following example uses the params
prop passed in from react-router
to get the id out of the path and append it to the requestName.
;; const REQUEST_GET_TODO = `REQUEST_GET_TODO_`; { return // ... } MyTodoComponent;
This will map the request tracking props to your Component
Request tracking props
- pending boolean (a request has been dispatched, pending resolve)
- lastPage boolean (if link headers are being used, this will notify the component if the last page has been fetched)
- requestDispatched boolean (determins if a namespaced request has ever been dispatched)
Data accessors
Now that all of your request data is managed in redux state, we need a way to access it in out views. The following are data accessors that come out of the box with redux-request-tracker
, feel free to add your own or submit an isses if you would like to see any additions to this.
getRequest
Get request is your go to data accessor for determining the state of your request.
const getRequest from 'redux-request-tracker'; const mapStateToProps = { return todosRequest: ;};
getAppHistory
This data accessor is more useful if you're analyzing your async events.
const getAppHistory from 'redux-request-tracker'; const mapStateToProps = { return appHistory: ;};
Pagination accessors
These data accessors are responsible for parsing request pagination if supported. Check out the Pagination section for more information.
const getFirstPage getLastPage getNextPage getPrevPage from 'redux-request-tracker'; const mapStateToProps = { return nextPage: prevPage: firstPage: lastPage: ;};
Pagination
redux-request-tracker
automatically parses the Link Header if your server supports it. GitHub has a good example of how this works.
Roadmap
- I want to add some components that are useful for working with the request history object returned from
getAppHistory
.
Suggestions?
Feel free to reach out with feedback and suggestions for improvement.