Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
redux-query
Advanced tools
A library for querying and managing network state in React/Redux applications
redux-query
is a library for querying and managing network state in React/Redux applications.
With redux-query
you can:
Install redux-query
via npm:
$ npm install --save redux-query
Add the entitiesReducer
and queriesReducer
to your combined reducer.
Include the queryMiddleware
in your store's applyMiddleware
call. queryMiddleware
requires two arguments: a selector (or function) that returns entities state, and a function for the queries state.
For example:
import { applyMiddleware, createStore, combineReducers } from 'redux';
import { entitiesReducer, queriesReducer, queryMiddleware } from 'redux-query';
import createLogger from 'redux-logger';
export const getQueries = (state) => state.queries;
export const getEntities = (state) => state.entities;
const reducer = combineReducers({
entities: entitiesReducer,
queries: queriesReducer,
});
const logger = createLogger();
const store = createStore(
reducer,
applyMiddleware(queryMiddleware(getQueries, getEntities), logger)
);
There are several interactive demos at https://amplitude.github.io/redux-query:
After making code changes, click the "Run" button above the code editor to view your changes.
Most of these demos use a mock server, which lets you also experiment with different server logic. Click on the "Mock Server" tab above the code editor to view and modify the mock server logic. In addition, you can view the log of redux actions and states by clicking on the "Redux Log" tab.
All dependencies are listed in package.json
. Redux and React are peer dependencies. HTTP requests are made using superagent.
There are two types of queries with redux-query
: "requests" and "mutations". Requests are for reading values from HTTP endpoints. Mutations are for HTTP endpoints that change network state – the "C", "U", and "D" in "CRUD".
Requests can be triggered from the connectRequest
higher-order component or a requestAsync
action. Mutations are triggered by dispatching a mutateAsync
action.
By default, requests are GETs and mutations are POSTS.
Query configs are objects used to describe how redux-query should handle the request or mutation. Query config options differ slightly between requests and mutations
Name | Type | Required? | Description |
---|---|---|---|
url | string | yes | The URL for the HTTP request. |
transform | function | Function that transforms the response data to an entities object where keys are entity IDs and values are entity data. Can be used to normalize data. | |
update | object | yes | Object where keys are entity IDs and values are update functions. |
body | object | The request body. For GETs, this object is stringified and appended to the URL as query params. | |
force | boolean | Perform the request even if we've already successfully requested it. | |
queryKey | string | The identifier used to identify the query metadata in the queries reducer. If unprovided, the url and body fields are serialized to generate the query key. | |
meta | object | Various metadata for the query. Can be used to update other reducers when queries succeed or fail. | |
options | object | Options for the request. Set options.method to change the HTTP method, options.headers to set any headers and options.credentials = 'include' for CORS. |
Name | Type | Required? | Description |
---|---|---|---|
url | string | yes | The URL for the HTTP request. |
transform | function | Function that transforms the response data to an entities object where keys are entity IDs and values are entity data. Can be used to normalize data. | |
update | object | yes | Object where keys are entity IDs and values are update functions. |
optimisticUpdate | object | Object where keys are entity IDs and values are functions that provide the current entity value. The return values are used to update the entities store until the mutation finishes. | |
body | object | The HTTP request body. For GETs, this object is stringified and appended to the URL as query params. | |
queryKey | string | The identifier used to identify the query metadata in the queries reducer. If unprovided, the url and body fields are serialized to generate the query key. | |
meta | object | Various metadata for the query. Can be used to update other reducers when queries succeed or fail. | |
options | object | Options for the request. Set options.method to change the HTTP method, options.headers to set any headers and options.credentials = 'include' for CORS. |
transform
functionstransform
functions let you process and normalize response data before it is passed to the update
step. They have the following signature:
(responseJson: ?Object, responseText: string) => { [key: string]: any }
If your data is normalized on the server, you may not need to use this function.
update
functionsupdate
functions are responsible for reconciling response data with the existing entities
reducer data for the given entity ID. They have the following signature:
(prevValue: any, transformedValue: any) => any
The prevValue
is the whatever value is selected from the entities
reducer for the respective entity ID. The returned value from this function will become the new value for the entity ID in the entities
reducer.
optimisticUpdate
functionsoptimisticUpdate
functions are just like update functions except they only pass the prevValue
:
(prevValue: any) => any
connectRequest
Use the connectRequest
higher-order component to declare network dependencies for a React component. connectRequest
takes a function that transforms the component props
to a request query config or an array of request query configs. Example usage:
import { connectRequest } from 'redux-query';
import { connect } from 'react-redux';
class Dashboard extends Component {
...
}
const DashboardContainer = connectRequest((props) => ({
url: `/api/dashboard/${props.dashboardId}`,
update: {
chartsById: (prevCharts, dashboardCharts) => ({
...prevCharts,
...dashboardCharts,
}),
dashboardsById: (prevDashboards, dashboards) => ({
...prevDashboards,
...dashboards,
}),
},
}))(Dashboard);
const mapStateToProps = (state, props) => {
return {
dashboard: getDashboard(state, props),
};
};
export default connect(mapStateToProps)(DashboardContainer);
connectRequest
passes an extra prop to the child component: forceRequest
. Calling this function will cause the request(s) to be made again. This may be useful for polling or creating an interface to trigger refreshes.
mutateAsync
Dispatch mutateAsync
Redux actions to trigger mutations. mutateAsync
takes a mutation query config as its only argument. Example usage with a react-redux-connected component:
// src/queries/dashboard.js
export const createUpdateDashboardQuery = (dashboardId, newName) => ({
url: `/api/${dashboardId}/update`,
body: {
name: newName,
},
update: {
dashboardsById: (prevDashboardsById, newDashboardsById) => ({
...prevDashboardsById,
...newDashboardsById,
}),
},
});
// src/actions/dashboard.js
import { mutateAsync } from 'redux-query';
import { createUpdateDashboardQuery } from '../queries/dashboard';
export const updateDashboard = (dashboardId, newName) => {
return mutateAsync(createUpdateDashboardQuery(dashboardId, newName));
};
// src/selectors/dashboard.js
export const getDashboard = (state, { dashboardId }) => {
if (state.entities.dashboardsById) {
return state.entities.dashboardsById[dashboardId];
} else {
return null;
}
};
// src/components/Dashboard.jsx
import { connect } from 'react-redux';
import { updateDashboard } from '../actions/dashboard';
import { getDashboard } from '../selectors/dashboard';
class Dashboard extends Component {
...
}
const mapStateToProps = (state, props) => {
return {
dashboard: getDashboard(state, props),
};
};
const mapDispatchToProps = (dispatch, props) => {
return {
changeName: (newName) => {
dispatch(updateDashboard(props.dashboardId, newName));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Dashboard);
When dispatching a mutateAsync
action, you can Promise-chain on the returned value from dispatch
:
const mapDispatchToProps = (dispatch, props) => {
return {
changeName: (newName) => {
dispatch(updateDashboard(props.dashboardId, newName)).then((result) => {
if (result.status !== 200) {
dispatch(showUpdateDashboardFailedNotification(props.dashboardId));
}
});
},
};
};
The result of the promise returned by mutateAsync
will be the following object:
Name | Type | Description |
---|---|---|
status | number | HTTP status code. |
body | object or null | Parsed response body. |
text | string | Unparsed response body string. |
duration | number | The total duration from the start of the query to receiving the full response. |
When the mutation succeeds, it will also include the following fields:
Name | Type | Description |
---|---|---|
transformed | any | Result from the transform function. Will be identical to body if transform is unprovided in the query config. |
entities | object | The new, updated entities that have been affected by the query. |
requestAsync
Similarly to how mutations are triggered by dispatching mutateAsync
actions, you can trigger requests by dispatching requestAsync
actions with a request query config.
You can also Promise-chain on dispatched requestAsync
actions, but a Promise will only be returned if redux-query
determines it will make a network request. For example, if the query config not have force
set to true
and a previous request with the same query key previously succeeded, then a Promise will not be returned. So be sure to always check that the returned value from a dispatched requestAsync
is a Promise before interacting with it.
redux-query
provides some useful selectors for reading from the queries reducer state:
Selector Name | Return Type | Description |
---|---|---|
isFinished | ?boolean | Returns true if the query was resolved or cancelled. |
isPending | ?boolean | Returns true if the query is in-flight – not resolved and not cancelled. |
status | ?number | Response HTTP status code. |
lastUpdated | ?number | Time at which the query was resolved. |
queryCount | ?number | Number of times a query was started with the same query key. |
All of the query selectors have the following signature:
(queryConfig) => (queriesReducerState) => mixed
redux-query
provides another reducer for applications that want to track response body, text, and headers in redux state. Unlike the entities and queries reducers, errorsReducer
is totally optional. If you include this reducer in your application's combined reducer, all responses from requests and mutations with non-2xx status codes will be recorded in this state.
Note: If your application has many queries that could potentially error and is used for long periods of time, you should avoid using this reducer as it could potentially accumulate a lot of memory usage. You can alternatively build your own reducer to track a subset of queries, or rely on the promise interface to handle error responses in an ad-hoc manor.
You can query from this state using the provided errorSelectors
:
Selector Name | Return Type | Description |
---|---|---|
responseBody | ?Object | Parsed response body (if query failed). |
responseText | ?string | Unparsed response body string (if query failed). |
responseHeaders | ?Object | Response headers (if query failed). |
All of the query selectors have the following signature:
(queryConfig) => (errorsReducerState) => mixed
By default, redux-query
makes XHR requests using the superagent library. If you'd rather use a different library for making requests, you can use redux-query
's queryMiddlewareAdvanced
middleware.
If you use a custom network interface and want to avoid including superagent in your bundle, change all of your imports from redux-query
to redux-query/advanced
.
Note: The default queryMiddleware
exported from the main redux-query
entry point is simply a superagent network interface bound to queryMiddlewareAdvanced
.
Network interfaces have the following interface:
type NetworkInterface = (
url: string,
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
config?: { body?: string | Object, headers?: Object, credentials?: 'omit' | 'include' } = {},
) => NetworkHandler;
type NetworkHandler = {
execute: (callback: (err: any, resStatus: number, resBody: ?Object, resText: string, resHeaders: Object) => void) => void,
abort: () => void,
};
Example queryMiddlewareAdvanced
usage:
import { applyMiddleware, createStore, combineReducers } from 'redux';
import { entitiesReducer, queriesReducer, queryMiddlewareAdvanced } from 'redux-query/advanced';
// A function that takes a url, method, and other options. This function should return an object
// with two required properties: execute and abort.
import myNetworkInterface from './network-interface';
export const getQueries = (state) => state.queries;
export const getEntities = (state) => state.entities;
const reducer = combineReducers({
entities: entitiesReducer,
queries: queriesReducer,
});
const store = createStore(
reducer,
applyMiddleware(queryMiddlewareAdvanced(myNetworkInterface)(getQueries, getEntities))
);
A fork of the redux
Async example is included. To run, first build the package:
$ npm install
$ npm run build
Then you can run the example:
$ cd examples/async
$ npm install
$ npm run start
FAQs
A library for querying and managing network state in Redux applications
The npm package redux-query receives a total of 13,832 weekly downloads. As such, redux-query popularity was classified as popular.
We found that redux-query demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 7 open source maintainers 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’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.