Two important technologies, React Query and Redux, play vital roles in ensuring that an application performs well and is easy to maintain in the ever-changing React development ecosystem. This article delves into react query with redux as well as their unique functionalities, stressing why they are important in achieving simplified data processing and scalable codebases.
React Query is a declarative framework for making remote data fetching, caching, and server-side data synchronization much easier. On the other hand, Redux is a sophisticated state management package that centralizes the application state using unidirectional data flow, guaranteeing predictability and traceability.
In this introduction to React Query and Redux, we’ll have a comprehensive but concise sweep of each library’s fundamentals, merits, and demerits and how the two can be intermarried to get the best of both worlds.
Table of Contents
React Query with Redux: Fundamentals
React Query is a Javascript library primarily for caching, managing, and updating local and remote data in React apps. It presents a pool of utilities that streamline the processes of caching, state management, and data fetching.
Let’s take a deep dive into the fundamentals of React Query, channeling our focus on caching, data fetching, and asynchronous actions.
Data Fetching with ‘use query’
React Query utilizes the ‘useQuery’ hook for data fetching. It captures the query key and a function responsible for data fetching.
Example:
(jsx)
import { useQuery } from ‘react-query’;
const { data, error, isLoading } = useQuery(‘myQueryKey’, fetchDataFunction);
Caching and Stale-WHile-Revalidating
React Query utilizes a stale-while-revalidate strategy and automatic data caching. Cached data is immediately used and re-fetch is activated in the background.
Example:
(jsx)
const { data } = useQuery(‘myQueryKey’, fetchDataFunction, {
staleTime: 60000, // Cache data for 60 seconds
refetchOnWindowFocus: false, // Disable automatic refetching on window focus
});
Invalidations and Manual Refetching
‘useQuery’ has a ‘refetch’ function which you can use to trigger a refetch manually.
Example:
(jsx)
const { data, refetch } = useQuery(‘myQueryKey’, fetchDataFunction);
const handleButtonClick = () => {
refetch();
};
See Also: Passing Functions As Props In React: Full Guide
Mutations for Data Modification
The function ‘useMutation’ is used for async actions that alter data on the server.
Eg.:
(jsx)
import { useMutation } from ‘react-query’;
const mutation = useMutation(updateDataFunction, {
onSuccess: () => {
queryClient.invalidateQueries(‘myQueryKey’); // Invalidate and refetch related queries
},
});
Automatic Caching
Automatic caching is a phenomenon whereby React Query automatically caches query results, narrowing down the need for manual caching tactics. Cache data is placed in memory, and the library cleverly manages background re-fetching and stale data.
Caching is essential in that it boosts application performance by avoiding unimportant network requests.
Intuitive API
React Query opens doors to hooks like ‘useMutation’ and ‘useQuery’. These are intuitive, plus simple API hooks used to fetch and update data.
This API tool is designed to be developer-friendly, easy to integrate with React components, and very easy to understand. As queries are defined declaratively, the library manages the hidden complexities of caching and data fetching.
Understanding Redux
Redux is a state management library that is commonly coupled with React applications. Other Javascript frameworks can integrate with Redux for its centralized and predictable manner of operation.
Store
A store is an object that houses the whole state of your application. It is a centralized single unit with the duty to maintain the state tree. It’s role extends further to allow or render permission to other components, giving them access and the ability to update the tree.
Actions
These are plain Javascript objects that stand as event representatives in the application. Actions should have a ‘type’ property that outlines, defines, and depicts the type of action being performed. Action creators are the functions responsible for creating activities.
Reducers
These functions dictate how the application’s state changes due to an action performed. Reducers take the current state and the action as inputs or arguments, then return the required new state. Eventually, the reducer functions are combined into a root reducer to define the comprehensive state of the application.
Dispatch
This Redux Store technique is employed to dispatch actions and activate state changes. Action dispatching enables components to interact with the store.
(javascript)
store.dispatch(increment());
Selectors
Selectors are functions designed for specific data extraction from the state and facilitate decomposing the state’s structure into constituent parts. This makes it easier to access relevant data.
(javascript)
const getCounter = (state) => state.counter;
Middleware
Middleware makes it possible to interact with actions before they reach reducers. The tasks middleware typically performs include side effect handling, logging, and asynchronous actions.
(javascript)
import { applyMiddleware, createStore } from ‘redux’;
import thunk from ‘redux-thunk’;
const store = createStore(rootReducer, applyMiddleware(thunk));
Comparing React Query and Redux
React Query and Redux are popular libraries for handling states in React applications, but their functions and use cases differ. Here is a comparison of React Query with Redux based on essential factors.
Data Fetching
React Query:
Offers a declarative method for obtaining data through the ‘useQuery’ hook. Based on the state of the data, it handles caching, background refetching, and state updates.
Redux:
Usually, it needs middleware to handle asynchronous tasks like data fetching (e.g., Redux Thunk). Developers must explicitly manage the state transition, and caching needs to be implemented separately.
State Management
React Query:
Mostly concerned with controlling the status of distant data. A query cache holds the data, and the library offers hooks to communicate with it.
Redux:
Intended for centrally controlling the state of an application as a whole. Reducers handle every state transition, and the store gives the application a single source of truth.
Changes and Adverse Reactions
React Query:
Offers a Mutation hook to manage changes in data. Allows for the automatic re-fetching of linked queries following a mutation and supports optimistic updates.
Redux:
Needs middleware (like Redux Thunk) to manage asynchronous operations, such as changing data. Developers must manage adverse effects.
Ease of Use
React Query:
It has an easy-to-use API to manage remote states and work with asynchronous data. It makes complicated situations like optimistic updating, caching, and prefetching simple.
Redux:
Compared to React Query, boilerplate code and initial setup may be more involved—calls for writing middleware, reducers, and action creators for asynchronous actions.
Community and Ecosystem
React Query:
Quickly becoming popular, emphasizing cutting-edge React development techniques. A vibrant community with extensive documentation.
Redux:
Well-known and extensively used within the React community. A vast ecosystem featuring a range of extensions, middleware, and development tools.
Pros and Cons of React Query and Redux
React Query
Merits
Declarative Data Fetching:
React Query streamlines the process of fetching data by offering a declarative API via hooks like ‘useQuery’,
Automatic Caching:
The library reduces the need for human state management and boosts efficiency by handling caching automatically,
Optimistic Updates:
This feature lets you update the user interface (UI) optimistically before the server verifies the data.
Query Invalidation:
This feature ensures that data is always current by allowing users to invalidate manually and prefetch queries.
Integration With React Suspense:
Works well with React Suspense, enabling components to suspend while data is being fetched, thus offering a seamless user experience.
DevTool Support:
It provides a DevTools add-on for monitoring and troubleshooting queries while developing.
Pagination and Infinite Loading:
Large dataset handling is easier with built-in pagination and infinite loading support.
Drawbacks
Learning Curve:
While the fundamentals are simple, comprehending and utilizing advanced features may take time and investigation.
Focused on Data Fetching:
React Query’s primary goal is to manage remote data; you may require other tools for more comprehensive state management.
Redux
Merits
Centralized State
Redux offers a centralized store that makes managing the state easier by acting as a single source of truth for the whole program.
Predictable State Changes
Pure functions (reducers) handle state changes to ensure predictability and ease of debugging.
Middleware Support
Middleware, such as Redux Thunk, allows for the controlled handling of asynchronous operations and side effects.
Large Ecosystem
Redux offers flexibility for a wide range of use cases thanks to its enormous ecosystem, which includes many middleware options, development tools, and extensions.
Time-Travel Debugging
Redux DevTools’ time travel debugging feature enables you to examine and replay state changes over time.
Community and Resources
Redux is an established library with a sizable and vibrant community offering many tutorials and documentation.
Drawbacks
Boilerplate Code
When implementing Redux, boilerplate code is frequently written for action creators, actions, reducers, and connecting components.
Complexity for Simple Scenarios
Redux may add needless complexity to small-to-medium-sized applications compared to more straightforward state management solutions.
Learning Curve
It may take some time and effort for beginners to understand the fundamentals of Redux. And it is recommended to get fluent with the basics before moving on to advanced concepts like Redux Selectors.
Asynchronous Curve
Although middleware can handle asynchronous actions, it may become challenging to manage asynchronous flows.
Real World Integration: Migrating Redux to React Query
Redux optimistic updates entail updating the user interface before the server confirms the outcome of a dispatched action (like a network request). The steps involved in moving from a Redux setup to an optimistic update strategy with React Query are as follows:
Redux Setup With Optimistic Updates (Before Migration)
Considering you have a standard Redux configuration consisting of reducers, actions, and middleware for async actions
Action Creators:
(javascript)
// actions.js
export const updateData = (data) => ({
type: ‘UPDATE_DATA’,
payload: data,
});
Reducer
(javascript)
// reducer.js
const initialState = {
data: null,
loading: false,
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case ‘UPDATE_DATA’:
return {
…state,
data: action.payload,
};
default:
return state;
}
};
export default reducer;
Middleware (eg., Thunk)
(javascript)
// middleware.js
export const updateDataAsync = (data) => {
return (dispatch) => {
dispatch({ type: ‘UPDATE_DATA’, payload: data });
// Simulate a network request
setTimeout(() => {
// Dispatch the result from the server
dispatch({ type: ‘UPDATE_DATA_SUCCESS’, payload: data });
}, 1000);
};
};
Migration Steps to React Query
Install React Query
(bash)
npm install react-query react-query/devtools
Update Components
(javascript)
// MyComponent.js
import React from ‘react’;
import { useMutation, useQueryClient } from ‘react-query’;
const MyComponent = () => {
const queryClient = useQueryClient();
const mutation = useMutation(
(newData) => {
// Simulate a network request
return new Promise((resolve) => {
setTimeout(() => {
resolve(newData);
}, 1000);
});
},
{
onSuccess: (newData) => {
// Optimistically update the UI
queryClient.setQueryData(‘myQueryKey’, newData);
},
}
);
const handleButtonClick = () => {
const newData = /* get new data */;
mutation.mutate(newData);
};
return (
<div>
<button onClick={handleButtonClick}>Update Data</button>
</div>
);
};
export default MyComponent;
Remove Redux Actions and Middleware
(javascript)
// actions.js
// Remove the updateData action creator
// middleware.js
// Remove the updateDataAsync middleware
Remove Redux Reducer
(javascript)
// reducer.js
// Remove the reducer file or remove the part related to UPDATE_DATA
Remove Redux Store and Provider Setup
(javascript)
// index.js
// Remove the Redux store setup and Provider component
Remove Redux Imports in Components
Eliminate any last Redux imports from your components.
Install and Integrate React Query DevTools (Optional)
(bash)
npm install react-query/devtools
Incorporate React Query DevTools to track and troubleshoot queries while they are being developed.
(javascript)
// react-query-config.js
import { ReactQueryDevtools } from ‘react-query/devtools’;
// … (your existing React Query setup)
export const ReactQueryProvider = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools />
</QueryClientProvider>
);
Converting Redux Store and Saga to React Query
Changing from a more conventional state management strategy to React Quary’s declarative API for data fetching, caching and mutations is necessary when converting a Redux store and Saga setup. Here’s a detailed how-to:
Install React Query:
Install the DevTools and the React Query library
Create a React Query Provider
To wrap your application, set up a QueryClient and QueryClientProvider. This will be the Redux Provider’s replacement.
Determining Redux Actions and Sagas
Determine which actions and sagas in your Redux setup are responsible for retrieving, updating, and managing side effects.
Replace Redux Actions with React Query Hooks
To handle data fetching and mutations, replace Redux actions with React Query Hooks (useQuery, useMutations, etc.).
Convert Redux Sagas to React Query Mutations
Move the logic from Redux Sagas to React Query Mutations to handle data mutations.
Remove Redux Store Setup and Reducers
Eliminate any related Redux Store and reducer setup.
Refactor Components
Reorganize components to connect to the Redux store via React Query hooks instead.
Optimistic Updates
Use React Query mutations on the Success callback to implement optimistic updates.
Test and Optimize
Ensure data fetching, catching optimistic updates, and mutations function as intended by thoroughly testing your application.
Code should be optimized using the new React Query-based configuration.
RTX Query: Bridging React Query with Redux
Redux toolkit is the official set of tools for creating Redux applications. The provision of a collection of utilities to minimize boilerplate code is intended to improve the development of Redux applications.
RTK Query is one of the Redux Toolkit libraries for data fetching and state management. Its goal is to make managing the associated states in a Redux application and submitting API requests easier.
Global State Management
RTK Query uses Redux for global state management by default because it is a component of the Redux Toolkit. It easily interfaces with the current Redux Store, giving you a single location to manage global and API-related states.
Shared Redux Store
You can use the same Redux Store for RTK and React Query. Depending on your needs and preferences, you can use RTK Query for some parts of data fetching and React Query for others with this integration.
Setting up and Configuring RTK Query in React Applications
Now let’s review the basic procedures for configuring RTK Query in a React application.
Install Required Packages
Installing the required packages should come first. You should install both RTK Query and Redux Toolkit since they are related.
(bash)
npm install @reduxjs/toolkit react-redux
Configure Redux Toolkit and RTK Query
Using the configStore function from @reduxjs/toolkit, create a redux store. This function accepts an object with properties that are reducers. Incorporate the @reduxjs/toolkit/query/react api reducer.
(javascript)
import { configureStore } from ‘@reduxjs/toolkit’;
import { api } from ‘./api’; // Define your API slices in the api file
const store = configureStore({
reducer: {
[api.reducerPath]: api.reducer,},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(api.middleware),
});
export default store;
Create API Slice
Utilize createSlice from @reduxjs/toolkit to define your API slices. RTK Query will automatically generate the required reducers and actions using these slices for various API components.
(javascript)
// api.js
import { createApi, fetchBaseQuery } from ‘@reduxjs/toolkit/query/react’;
export const api = createApi({
reducerPath: ‘api’,
baseQuery: fetchBaseQuery({ baseUrl: ‘/api’ }), // Replace with your API base URL
endpoints: (builder) => ({
// Define your API endpoints here
getUsers: builder.query({
query: () => ‘users’,
}),
// Other endpoints…
}),
});
export const { useGetUsersQuery } = api;
Create React Query Provider
Build a React Query Provider to encapsulate your whole application for simple debugging. Include the ReactQueryDevtools and QueryClientProvider.
(javascript)
// react-query-config.js
import { QueryClient, QueryClientProvider } from ‘react-query’;
import { ReactQueryDevtools } from ‘react-query/devtools’;
const queryClient = new QueryClient();
export const ReactQueryProvider = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
<ReactQueryDevtools />
</QueryClientProvider>
);
Wrap Your App With Providers
Use the React Query Redux store providers throughout your entire application
(javascript)
// index.js
import React from ‘react’;
import ReactDOM from ‘react-dom’;
import { Provider } from ‘react-redux’;
import { ReactQueryProvider } from ‘./react-query-config’;
import store from ‘./store’;
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<ReactQueryProvider>
<App />
</ReactQueryProvider>
</Provider>
</React.StrictMode>,
document.getElementById(‘root’)
Advanced Implementation: Authentication and API Integration
Redux offers a centralized store for managing application state, while React Query is especially effective at handling data fetching and caching.
Here are some things to consider when utilizing Redux and React Query to combine authentication and API calls, including addressing how to efficiently update the state when props change in React, which is crucial for the seamless integration of these technologies.
Authentication
React Query For Authentication API Calls
For handling authentication API calls, use React Query. Login/logout processes can benefit from the useMutation hook.
Token Management With Redux
Utilize the Redux store to manage authentication tokens (like JWT). Dispatched actions to the token in the Redux state after successful authentication.
Redux For Authentication State
To handle the authentication state, employ Redux. Define redux action and reducers to manage authentication-related state changes, like login, logout, and token expiration.
Global Authentication State
Maintain global access to the Redux store’s authentication state. This enables various components within the application to respond to modifications in the authentication status.
React Query Devtools
During development, use the React DevTools to examine and troubleshoot the status of queries and mutations related to authentication.
API Integration
React Query For Data Fetching
Make use of React Query to retrieve data from your API. The useQuery hook makes refetching, caching, and data fetching easier.
Redux For Global Application State
Use Redux to handle the state of your global application. Redux can manage a state that needs sharing between components or states unrelated to APIs, while react query handles state related to APIs.
Integrate React Query With Redux
Use the react-query/redux package to integrate React Query with the Redux store. This package contains utilities for sending React Query actions into the Redux store.
Middleware For API Requests
Make sure Redux Thunk or any other middleware you use for API requests works well with React Query. Keep your hands off React Query’s internal logic is meant to handle its query lifecycle.
Redux DevTools For API Actions
Utilize the Redux Devtools Extension to track and troubleshoot Redux actions connected to API requests to monitor the progression of events during async operations. This is especially helpful.
User Interface Integration and Middleware Components
Middleware Component for User Authentication
Create Middleware Function
Establish a middleware function that will intercept commands sent to the Redux store.
Check Authentication Actions
In the middleware, determine whether the intercepted action had anything to do with user authentication. This can apply to any action about authentication, including login, logout, and token f=refresh.
Access Redux Store State
To obtain data about user authentication, including tokens, user details, and authentication status, one can retrieve the present state of the Redux store.
Handle Authentication Logic
Using the intercepted actions as a basis, implement the logic for user authentication. This could entail launching more processes, contacting APIs, or updating the Redux store with the user’s current authentication state.
See Also: Mastering Redux-Saga Typescript: Full Guide
React Query Integration
Dispatch Actions for React Query
Use the middleware’s dispatch actions to communicate with React Query. For instance, based on changes related to authentication, you may wish to update the React Query cache, initiate refetches, or INvalidate particular queries.
Access React Query Hooks
Access React Query hooks directly within the middleware to initiate queries or mutations about authentication.
Handle API Request and Tokens
Include Tokens in API Requests
Adjust API requests inside the middleware to include the required authentication tokens in the headers if your authentication uses tokens (such as JTW).
Token Refresh
Put the middleware’s token refresh logic into action. Dispatch actions to refresh tokens and update the Redux store with the refreshed tokens when they expire.
See Also: ReactJS – prevState in the new useState React hook?
Error Handling
Handle Authentication Errors
When performing tasks related to authentication, implement error handling. To display error messages in the user interface, dispatch actions to update the Redux store with error information.
Dispatch Additional Actions
Dispatch Additional Actions
Send out further actions as necessary based on the flow of authentication. For Instance, send out commands to reroute the user following a successful login or to clear a specific state following a logout.
Integrate With the Redux Store
Connect Middleware To Redux
When setting up the store, integrate the middleware with the Redux store. The authentication middleware can be included by using Redux’s applyMiddleware function.
(javascript)
// Example store setup with middleware
import { createStore, applyMiddleware } from ‘redux’;
import rootReducer from ‘./reducers’;
import authenticationMiddleware from ‘./middleware/authenticationMiddleware’;
const store = createStore(
rootReducer,
applyMiddleware(authenticationMiddleware)
);
export default store;
See Also: Introduction To Redux Toolkit Testing
When handling asynchronous actions in Redux, developers often have to choose between Redux Thunk and Redux Saga.
FAQs
What is the difference between Redux Toolkit and RTK query?
Redux Toolkit is a package to streamline Redux-related operations such as action creation, state management, and reducer logic. RTX Query is a library that improves remote data management in a React application backed by Redux.
What is the RTK query used for?
Redux Toolkit Query is a library part of the Redux Toolkit used for data fetching and caching. It is made to operate smoothly with Redux and offers tools for managing remote data in a React application powered by Redux.
Why do we use Query in React?
As the name suggests, Query React is designed to request data remotely. It is used to attain optimistic updates, background data prefetching, declarative query configuration, automatic caching, and streamlined data fetching.
What is the difference between Axios and React queries?
Axios is a Javascript library created to make HTTP requests. Usually, its function is to communicate with APIs and perform data-fetching tasks from servers. On the other hand, Redux Query, though a library, is made specifically to manage remote and async data. It also allows easy operations like caching and data fetching to be performed.
Conclusion
In conclusion, there are differences between React Query and Redux, and utilizing one or the other will rely on your application’s complexity and particular needs. React Query with Redux combined is a popular and useful way to handle local and remote state management requirements.
We don’t always have to juxtapose React Query with Redux. They don’t always have to conflict with one another; in fact, they frequently work best together.
See Also: Understanding React Spread Props