Supercharge Your Typescript React Application with These Essential Utilities

Radovan Stevanovic
Level Up Coding
Published in
9 min readFeb 1, 2023

--

Utils are functions used to perform specific tasks in a software application. They are designed to be reusable, flexible, and easy to maintain. This blog post will discuss a set of utils that can be used to supercharge your Typescript React application.

These utils are designed to make common tasks easier, improve code readability, and make your code more maintainable.

isEnum

export const isEnum = <T, R>(e: T, value: R): boolean =>
Object.values(e).includes(value);

The isEnum function takes two arguments: value and enumeration. The value argument is the value that needs to be checked if it exists within the enumeration. The enumeration argument is an object that contains a set of values that are considered valid values.

The function returns a boolean value indicating whether the value exists within the enumeration or not. The use of a type guard with value is keyof typeof enumeration ensures that the type of value is narrowed to a value that exists within the enumeration.

Here’s an example of how you might use the isEnum util in your code:

enum UserRole {
ADMIN = 'ADMIN',
USER = 'USER',
}

const userRole = UserRole.ADMIN;

console.log(isEnum(UserRole, userRole)); // true

In this example, the isEnum util is used to check if the userRole belongs to the UserRole enum. The function returns true, indicating that the userRole is indeed a member of the UserRole enum.

The isEnum util is a simple, yet powerful tool that can help you write more efficient and robust code by reducing the need for manual checks and making your code more readable.

extractQueryParams

The next util is the extractQueryParams function is a helper for extracting query parameters from a search string. This function takes in two parameters: the search string and a boolean flag "dry". When set to true, the "dry" flag will not convert the types of the extracted query parameters. The function returns an object with the extracted query parameters.

Here is the code for the extractQueryParams function:

export const extractQueryParams = <T>(search: string, dry = false): T => {
if (!search) return {} as T;

return search
.replace("?", "")
.split("&")
.reduce((data: T, item) => {
const [k, v] = item.split("=");

if (!k || !v) return data;

if (dry) return { ...data, [k]: v };

if (v.includes(",")) return { ...data, [k]: v.split(",") };

if (["true", "false"].includes(v)) return { ...data, [k]: v === "true" };

if (v === "null") return { ...data, [k]: null };

if (!Number.isNaN(+v)) return { ...data, [k]: +v };

return { ...data, [k]: v };
}, {} as T);
};

This function can be very useful when working with query parameters in a URL. It takes the search string, which is typically the part of the URL after the “?” symbol and returns an object with the extracted query parameters. This makes it much easier to access the query parameters in your code, as you can simply use the keys of the returned object to access the values. Additionally, the function also supports converting the values of the query parameters to different data types, such as arrays, booleans, numbers, and null. This can be useful when working with query parameters representing different data types.

Here’s an example of using the extractQueryParams utility:

const search = '?code=12345&name=John&active=true';
const queryParams = extractQueryParams<{ code: number, name: string, active: boolean }>(search);
console.log(queryParams);
// Output: { code: 12345, name: 'John', active: true }

As you can see, this utility allows you to extract query parameters from a search string and convert them into a typed object. In this example, the code parameter is converted to a number, name is converted to a string, and active is converted to a boolean.

You can also use the dry parameter to return the query parameters without type conversion:

const search = '?code=12345&name=John&active=true';
const queryParams = extractQueryParams<{ code: string, name: string, active: string }>(search, true);
console.log(queryParams);
// Output: { code: '12345', name: 'John', active: 'true' }

This utility can save you a lot of time and effort in parsing query parameters and provides a more robust and type-safe way.

delay

The next util in our set is the delay function. This function takes in a delay time in milliseconds and returns a Promise that resolves after the specified amount of time.

Here is the implementation of the delay function:

export const delay = (delayTime = 300): Promise<void> =>
new Promise((resolve) => setTimeout(resolve, delayTime));

Here is an example of how you can use the delay function:

async function delayedAction() {
console.log("Taking action!");
await delay(500);
console.log("Action taken!");
}

The benefits of using the delay function are numerous. Firstly, it helps to keep your code clean and readable. Instead of setting a setTimeout call and managing the callback, you can call the delay function and pass in the desired delay time. This makes it easier to understand what is happening in your code, especially for others looking at it. Secondly, it helps to make your code more flexible and reusable. Instead of writing setTimeout calls throughout your code, you can import the delay function and use it wherever you need to delay the execution of some code. This makes it easier to manage delays throughout your application and reduces the amount of code that needs to be written. Overall, the delay function is a simple yet powerful tool that can help you write better and more maintainable code.

cb

The cb utility is a simple utility that allows developers to pass callbacks in a lazy manner. It is a higher-order function that takes in a callback fn and its arguments, and returns a new function that can be invoked later to execute the original callback while providing type safety and maintaining the readability of the code

The implementation of the cb function is as follows:

export const cb = <T extends any[], V>(fn: (...args: T) => V, ...args: T): (() => V) => {
return () => fn(...args);
};

Here’s an example of how you can use the cb function:

<button onClick={cb(toggle, !isActive)}>Toggle</button>

The benefits of using the cb function are numerous. It makes it easy to delay the execution of a function until it is needed, which can improve the performance of your application. Finally, it can also make your code more readable by abstracting away the details of executing a callback and instead focusing on the logic of your application.

formatCurrency

The next utility in our set is formatCurrency. This utility is used to format numbers as currency, specifically US dollars. The function takes in two parameters: value and replaceDoubleZero. The value parameter is the number that needs to be formatted as currency, and the replaceDoubleZero parameter is a boolean that determines whether or not the ".00" at the end of the currency string should be removed.

Here’s the implementation of formatCurrency:

export function formatCurrency(value: number, replaceDoubleZero?: boolean) {
const formattedValue = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
}).format(value);
if (replaceDoubleZero) {
return formattedValue.replace(".00", "");
}
return formattedValue;
}

And here’s an example of how to use formatCurrency:

const value = 123456;
const formattedValue = formatCurrency(value, true);
console.log(formattedValue); // "$123,456"

The benefits of using formatCurrency are clear. Using this utility, you can easily format numbers as currency without having to write complex code or worry about locales and currency symbols. Additionally, by using the replaceDoubleZero parameter, you can remove the ".00" from the end of the currency string, which can be useful in certain scenarios. Overall, formatCurrency is a simple yet powerful tool for formatting numbers as currency in your TypeScript React applications.

capitalize

The capitalize utility function is a simple but effective tool for transforming strings. Its purpose is to take string input and return a new string with the capitalized first letter. Here is the implementation:

export const capitalize = (str: string): string => {
if (str.length === 0) {
return str;
}
return str[0].toUpperCase() + str.slice(1);
};

Here’s an example of how you might use the capitalize utility in your React application:

const name = "john doe";
const capitalizedName = capitalize(name);
console.log(capitalizedName); // "John Doe"

The benefits of using the capitalize utility is twofold. Firstly, it provides a clean and concise way to format strings, making your code more readable and maintainable. Secondly, it eliminates the need for you to write your implementation of string capitalization, freeing up time and resources to focus on other tasks. Whether you're working on a small project or a large enterprise application, the capitalize utility is valuable in your toolkit.

onChange and unpack

The next utility function we will discuss is onChange. This higher-order function takes an event handler and returns a new event handler that automatically unpacks the value of a change event. The onChange utility is particularly useful when working with React forms.

Here’s an implementation of the onChange function:

import { ChangeEvent } from 'react';

type E = ChangeEvent<
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
>;
export const unpackE = (e: E): string => {
return e.target.value;
};

export const onChange = (handler: (v: string) => void) => (e: E) =>
handler(unpackE(e));

Let’s see an example of how onChange can be used in a React component:

import React, { useState } from 'react';
import { onChange } from './utils';

const ExampleForm = () => {
const [value, setValue] = useState('');
return (
<input
type="text"
value={value}
onChange={onChange(setValue)}
/>
);
};

In this example, the onChange utility handles changes to the input element. When the input value changes, the onChange function calls the setValue function with the updated value. The onChange utility abstracts away the process of unpacking the value from the change event, making it easier to write clean and concise code.

Using onChange has several benefits. First, it makes it easier to write clean and concise code. Second, it eliminates the need to write repetitive code to unpack the value from change events. And finally, it makes it easier to manage change events in a React component, as the onChange utility can be reused across multiple components.

toHumanReadable

The toHumanReadable util is used to convert a snake_case or camelCase string into a human-readable format. The implementation is quite straightforward: it first replaces all instances of _ with spaces, then replaces instances of lowercase followed by uppercase with the lowercase letter followed by a space and the uppercase letter. The string is then converted to lowercase, and the first character of each word is capitalized.

Here’s an implementation of the toHumanReadable function:

export const toHumanReadable = (str: string) => {
return str
.replace(/_/g, " ")
.replace(/([a-z])([A-Z])/g, "$1 $2")
.toLowerCase()
.replace(/(^|\s)\S/g, function (t) {
return t.toUpperCase();
});
};

Let’s see an example of how toHumanReadable can be used:

import { toHumanReadable } from './utils';

const snake_case = 'snake_case';
const humanReadable = toHumanReadable(snake_case);
console.log(humanReadable); // Snake Case

parse

The parse utility function is used to parse a string representation of a JSON object into a strongly-typed object. The function takes two arguments: value, the string representation of the JSON object, and def, a default value to return if the parsing fails.

export const parse = <T>(value: string | undefined, def: T): T => {
if (!value) return def;
try {
return JSON.parse(value) as T;
} catch (e) {
return def;
}
};

Example:

const jsonString = '{"name": "John Doe", "age": 30}';
const parsedValue = parse<{name: string, age: number}>(jsonString, {name: '', age: 0});
console.log(parsedValue); // {name: 'John Doe', age: 30}

The parse utility function provides a convenient and reliable way to parse a string representation of a JSON object into a strongly-typed object. This helps avoid manual JSON string parsing and reduces the chance of runtime errors caused by incorrect parsing. The function also provides a default value to return if the parsing fails, making it a useful tool for handling edge cases in your application.

Final Words

In conclusion, these custom utilities offer a variety of functions that can greatly simplify the process of writing and maintaining code. From checking whether a value is an enumeration to parsing strings into JSON, these functions are designed to make the developer’s life easier. Using these utilities, developers can focus on writing their core functionality and let the utilities handle the rest. The benefits of using these utilities include less code to write, fewer bugs, and improved code readability. Overall, these utilities are a valuable addition to any developer’s toolkit.

Level Up Coding

Thanks for being a part of our community! Before you go:

🚀👉 Join the Level Up talent collective and find an amazing job

--

--

Curiosity in functional programming, cybersecurity, and blockchain drives me to create innovative solutions and continually improve my skills