React Query Data Transformations: Using the Select Option
Published September 24, 2023
Briefly explore the different approaches to data transformation in React Query and demonstrate an example of refactoring a image list component to use React Query's "select" option for data transformation.
- Summary of Other Data Transformation Options
- Initial Code Implementation
- Refactoring with the "Select" Option
- Links and references
React Query provides various options for data transformations, allowing developers to manipulate and format data according to their needs. One of the options available is the "select" option. In this post, I will explore the different approaches to data transformation in React Query. I'll demonstrate an example of refactoring a image list component to use React Query's "select" data transformation.
Summary of Other Data Transformation Options
Before diving into the "select" option, let's briefly summarize the other approaches to data transformation in React Query:
-
On the backend: This approach involves transforming the data on the server-side before it is sent to the frontend. It can be an ideal solution if you have control over the backend and can request the data in the desired format. However, it may not always be possible. In my case the private REST APIs are consumed by different products inside the company.
-
In the queryFn: The queryFn is the function passed to the
useQuery
hook. It allows you to modify the data before it is stored in the query cache. This approach provides flexibility in transforming the data to match your requirements. You can manipulate the data within thequeryFn
function before returning it. However you will not have access to the original structure. Though if you look at the react-query-devtools, you will see the transformed structure. -
In the component: React Query also allows you to perform data transformations directly in the component where the data is being used. This approach gives you the freedom to manipulate the data in the rendering phase, using JavaScript array methods or other techniques.
Initial Code Implementation
Initially, when working with React Query, we might have written our code without utilizing the "select" option. Let's take a look at a code snippet that demonstrates my initial implementation inside the component:
// ImageLibrary.tsx
const transformRows: IImageListItem[] = (rows as TypeImage[])?.map((item: TypeImage) => {
const { data, name, updated_at, ...rest } = item;
const thumbnail = data.sources.thumbnail ? (
<div className={styles.wrapText}>
<img src={data.sources.thumbnail} alt={name} />
</div>
) : null;
const imageName = <div className={styles.wrapText}>{name}</div>;
const fileType = data.format?.toUpperCase();
const dimensions = `${data.dimensions.width} x ${data.dimensions.height}`;
const imageSizeInBytes = data?.size || 0;
const imageSize = getImageSize(imageSizeInBytes);
const uploadDateTime = updated_at ? (
<DateDisplay dateFormat={DATE_AT_TIME_FORMAT} value={updated_at?.toString()} />
) : null;
return { ...rest, thumbnail, imageName, fileType, imageSize, dimensions, uploadDateTime };
});
In the above code, we receive the data as an array of rows from the parent component and then manipulate it within the component to format it according to our requirements. While this approach works, it can lead to code duplication if we need to perform similar transformations in multiple components.
Each transformed object has the following properties:
thumbnail
: A JSX element that displays the thumbnail image of the item, if it exists. If the item does not have a thumbnail image, this property is set tonull
.imageName
: A JSX element that displays the name of the item.fileType
: A string that represents the format of the item's data, converted to uppercase.dimensions
: A string that represents the dimensions of the item's data.imageSize
: A string that represents the size of the item's data, converted to a human-readable format.uploadDateTime
: A JSX element that displays the date and time when the item was last updated, if it exists. If the item does not have an update time, this property is set tonull
.
The getImageSize
function is used to convert the size of the item's data from bytes to a human-readable format.
Refactoring with the "Select" Option
To avoid code duplication and centralize the data transformation logic, we can refactor our code using the "select" option provided by React Query. The "select" option allows us to define a selector function that will be applied to the query data before it is returned to the component.
Here's an example of how we can implement a "select" option:
In the refactored code, we define a separate imageListTransform
function that takes the raw data as input and returns the transformed data. We then pass this function as the select
property to the useImageLibrary
hook. React Query automatically applies the selector function to the query data before returning it to the component.
By utilizing the select
property, we can keep our component code clean and concise, as the data transformation logic is now separated into a separate function. Optionally this can be passed in as a conditionally property based on the consuming component retaining the option to return the original data shape.
Before
After
Now transformRows
becomes a much thinner enrichRows
that focuses on providing JSX elements with the pre-transformed primitives. Additionally the interface IImageListItem
now only contains primitive properties.
// ImageLibrary.tsx
const enrichRows = (rows as IImageListItem[])?.map((item) => {
const thumbnail = item.thumbnail ? (
<div className={styles.wrapText}>
<img src={item.thumbnail} alt={item.imageName} />
</div>
) : null;
const imageName = <div className={styles.wrapText}>{item.imageName}</div>;
return { ...item, thumbnail, imageName };
});
// useImageLibrary.types.ts
export interface IImageListItem {
created_at?: Date;
archived?: boolean;
fileType?: string;
id: string;
slug: string;
space_id: string;
thumbnail: string;
original: string;
imageName: string;
imageSize: string;
dimensions: string;
uploadDateTime: string;
}
In conclusion, React Query provides various options for data transformations, and the "select" option is a powerful tool for centralizing and simplifying data manipulation logic. By using the "select" option, we can ensure code reusability, maintainability, and improved readability in our React Query applications.
Links and references
To learn more about the pros and cons of each approach, visit: https://react-query.tanstack.com/guides/data-transformations