Research
Security News
Quasar RAT Disguised as an npm Package for Detecting Vulnerabilities in Ethereum Smart Contracts
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
redux-requests
Advanced tools
Manages in-flight requests with a Redux reducer - avoid issuing duplicate requests without any special logic!
Manages in-flight requests with a Redux reducer - avoid issuing duplicate requests without any special logic!
npm install --save redux-requests
Say your application has two views for the same set of data, and this data has not yet been fetched. A naïve approach to fetch this data would be to trigger an Action Creator, which fetches the data from an HTTP API endpoint, in both of the views as soon as they render (componentWillMount
in React terms).
The problem with this approach is that you end up with two identical HTTP requests when you only need one! You waste bandwidth doing this, and you may also waste render cycles as the Store updates twice as a result of handling both identical responses.
You could wrap all your calls to fetch the data with if
statements, and keep track of that state somewhere, but who wants to do that by hand?
This library will not only keep track of all pending requests for you, but also provide a convenient middleware function that will avoid dispatching Actions to request data if there is already a pending HTTP request for this data in flight!
As a result, you can use the very same naïve approach outlined earlier with hardly any code changes and it will "just work"! Keep your views stateless and your Reducers ignorant of the notion of "pending requests"!
Just specify a function that makes the request (should return a Promise
), Action objects to dispatch depending on the outcome of the request, and register the createRequestMiddleware
middleware and the requestsReducer
reducer as part of your Redux configuration. That's it.
import { attemptRequest, requestsReducer, createRequestMiddleware } from 'redux-requests';
// Attempt to make a request if there isn't one for this URL already
function loadRepos(userId) {
// Using redux-thunk middleware here, but other options should work as well
return function (dispatch, getState) {
const url = `https://api.github.com/users/${userId}/repos`;
attemptRequest(url, {
begin: () => ({
type: 'LOAD_REPOS',
payload: {
userId
}
}),
success: response => ({
type: 'LOAD_REPOS',
payload: {
userId,
response
}
}),
failure: error => ({
type: 'LOAD_REPOS',
error,
payload: {
userId
}
})
}, () => fetch(url)
.then(checkStatus)
.then(parseJSON)
, dispatch);
}
}
// Add additional reducer and middleware
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware, createRequestMiddleware())(createStore);
let store = createStoreWithMiddleware(combineReducers({ requestsReducer, githubRepos }));
The attemptRequest
function is actually just a simple helper (and is completely optional). All it does is:
meta.httpRequest
fields to your Action objectsmeta.httpRequest.url
is required, and will be used as the unique identifier for the requestmeta.httpRequest.done
is a boolean indiecating if this action corresponds to a beginning or ending part of the request sequence
meta.httpRequest.done = true
dispatch
for your initial request Action was cancelled (dispatch
will return undefined
), and if so, prevent issuing the request// React component
class Repos extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
// Action Creator attempts to request data for this user
this.props.loadRepos(this.props.username);
}
render() {
return (
<div>
{ this.props.repos }
</div>
);
}
}
function mapStateToProps(state) {
return {
repos: state.githubRepos
};
}
function mapDispatchToProps(dispatch) {
return {
loadRepos: (userId) => { dispatch(loadRepos(userId)); }
};
}
export const ReposComponent = connect(mapStateToProps, mapDispatchToProps)(Repos);
// Action Creator
export function loadRepos(userId) {
return function (dispatch, getState) {
const url = `https://api.github.com/users/${userId}/repos`;
dispatch({
type: 'LOAD_REPOS',
payload: {
userId
}
});
fetch(url)
.then(response => dispatch({
type: 'LOAD_REPOS',
payload: {
userId,
response
}
}))
.catch(error => dispatch({
type: 'LOAD_REPOS',
error: true,
payload: {
userId,
error
}
})
);
}
}
// Store
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware)(createStore);
let store = createStoreWithMiddleware(combineReducers({ githubRepos }));
// React component stays exactly the same!
// Action Creator changes slightly
export function loadRepos(userId) {
return function (dispatch, getState) {
const url = `https://api.github.com/users/${userId}/repos`;
if (!dispatch({
type: 'LOAD_REPOS',
payload: {
userId
},
meta: {
// Add metadata to the action
httpRequest: { url, done: false }
}
})) {
return; // bail out here if the middleware cancelled the dispatch
}
fetch(url)
.then(response => dispatch({
type: 'LOAD_REPOS',
payload: {
userId,
response
},
meta: {
// Add metadata to the action
httpRequest: { url, done: true }
}
}))
.catch(error => dispatch({
type: 'LOAD_REPOS',
error: true,
payload: {
userId,
error
},
meta: {
// Add metadata to the action
httpRequest: { url, done: true }
}
})
);
}
}
// Add additional reducer and middleware
import { requestsReducer, createRequestMiddleware } from 'redux-requests';
const createStoreWithMiddleware = applyMiddleware(thunkMiddleware, createRequestMiddleware())(createStore);
let store = createStoreWithMiddleware(combineReducers({ requestsReducer, githubRepos }));
requestsReducer(state, action)
A reducer that keeps track of pending request state. It only operates on actions containing the meta.httpRequest
field.
createRequestMiddleware(stateSelectorFunction)
Returns a middleware function to pass to applyMiddleware
. Optionally pass a stateSelectorFunction
which returns where the requestsReducer
keeps its state in the Store (if not passed, will default to state => state.requests
).
Ex: applyMiddleware(createRequestMiddleware(state => state.pendingHttpRequests))(createStore)
attemptRequest(url, actions, makeRequest, dispatch)
Helper function to reduce boilerplate when issuing a request, while still allowing full control over the way in which the request is made/handled.
url
is the unique URL for this request.actions
should be an object with begin
, success
, and failure
methods; each of which return an Action object (but do not need to include the meta.httpRequest
information, as that will be added automatically).makeRequest
should return a Promise
(how you make/handle the request is up to you).dispatch
is function called when an Action is triggered (typically this will be the standard Redux store's dispatch
method).Inspired by the Marty fetch API.
FAQs
Manages in-flight requests with a Redux reducer - avoid issuing duplicate requests without any special logic!
We found that redux-requests demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket researchers uncover a malicious npm package posing as a tool for detecting vulnerabilities in Etherium smart contracts.
Security News
Research
A supply chain attack on Rspack's npm packages injected cryptomining malware, potentially impacting thousands of developers.
Research
Security News
Socket researchers discovered a malware campaign on npm delivering the Skuld infostealer via typosquatted packages, exposing sensitive data.