Published on
-
5 mins read

Mastering Array Manipulation in React with useArray Hook

Authors
  • avatar
    Name
    Seraj Vahdati
    Twitter
    @seraj

Introduction

Working with arrays in React can often become complex, especially when managing stateful arrays with frequent operations like adding, removing, or filtering elements. The useArray custom hook simplifies these operations and, when combined with TypeScript, provides strong type safety and better developer experience. In this post, we'll explore how to use useArray with TypeScript to streamline your React application development.

Why Use TypeScript with useArray?

TypeScript brings static typing to JavaScript, which helps catch errors early in the development process. By using TypeScript with useArray, you gain several benefits, including:

  • Type Safety: Ensures that array operations are performed on the correct data types.
  • Better IntelliSense: Provides auto-completion and inline documentation in your IDE.
  • Reduced Bugs: Catch potential issues during compile time rather than at runtime.

Setting Up useArray in TypeScript

First, import the useArray hook into your component:

import { useState } from 'react'
type UseArrayReturnType<T> = {
array: T[]
set: React.Dispatch<React.SetStateAction<T[]>>
push: (element: T) => void
filter: (callback: (element: T, index: number, array: T[]) => boolean) => void
update: (index: number, newElement: T) => void
remove: (index: number) => void
clear: () => void
pop: () => void
find: (callback: (element: T, index: number, array: T[]) => boolean) => T | undefined
findIndex: (callback: (element: T, index: number, array: T[]) => boolean) => number
includes: (element: T) => boolean
isEmpty: () => boolean
}
const useArray = <T>(initialValue: T[] = []): UseArrayReturnType<T> => {
const [array, setArray] = useState<T[]>(initialValue)
const push = (element: T) => setArray((a) => [...a, element])
const filter = (callback: (element: T, index: number, array: T[]) => boolean) =>
setArray((a) => a.filter(callback))
const update = (index: number, newElement: T) => {
setArray((a) =>
index >= 0 && index < a.length ? [...a.slice(0, index), newElement, ...a.slice(index + 1)] : a
)
}
const remove = (index: number) => {
setArray((a) =>
index >= 0 && index < a.length ? [...a.slice(0, index), ...a.slice(index + 1)] : a
)
}
const clear = () => setArray([])
const pop = () => {
setArray((a) => a.slice(0, a.length - 1))
}
const find = (callback: (element: T, index: number, array: T[]) => boolean) =>
array.find(callback)
const findIndex = (callback: (element: T, index: number, array: T[]) => boolean) =>
array.findIndex(callback)
const includes = (element: T) => array.includes(element)
const isEmpty = () => array.length === 0
return {
array,
set: setArray,
push,
filter,
update,
remove,
clear,
pop,
find,
findIndex,
includes,
isEmpty,
}
}
export default useArray
import useArray from './useArray'

Initialize the hook with a default array:

const MyComponent = () => {
const {
array,
push,
update,
remove,
clear,
pop,
find,
findIndex,
includes,
isEmpty,
} = useArray<number>([1, 2, 3])
// Your component logic here
}

Key Methods in useArray

  • push(element: T): Adds a new element to the array.

    push(4) // array: [1, 2, 3, 4]
  • update(index: number, newElement: T): Updates an element at a specified index.

    update(1, 10) // array: [1, 10, 3]
  • remove(index: number): Removes the element at the specified index.

    remove(0) // array: [2, 3]
  • filter(callback: (element: T, index: number, array: T[]) => boolean): Filters the array based on a callback function.

    filter((num) => num > 1) // array: [2, 3]
  • clear(): Clears the entire array.

    clear() // array: []
  • pop(): Removes the last element from the array.

    pop() // array: [1, 2]
  • find(callback: (element: T, index: number, array: T[]) => boolean): T | undefined: Finds the first element that matches the condition in the callback.

    const found = find((num) => num > 1) // found: 2
  • findIndex(callback: (element: T, index: number, array: T[]) => boolean): number: Finds the index of the first element that matches the condition in the callback.

    const index = findIndex((num) => num > 1) // index: 1
  • includes(element: T): boolean: Checks if the array includes a specific element.

    const isInArray = includes(2) // isInArray: true
  • isEmpty(): boolean: Checks if the array is empty.

    const empty = isEmpty() // empty: false

Example Use Case

Consider a situation where you're building a todo list. You can use useArray to manage the list of tasks:

type Todo = { id: number; text: string; completed: boolean }
const TodoList = () => {
const { array: todos, push, update, remove, clear } = useArray<Todo>([])
const addTodo = (text: string) => {
push({ id: Date.now(), text, completed: false })
}
const toggleTodo = (id: number) => {
const index = todos.findIndex((todo) => todo.id === id)
if (index !== -1) {
update(index, { ...todos[index], completed: !todos[index].completed })
}
}
const deleteTodo = (id: number) => {
const index = todos.findIndex((todo) => todo.id === id)
if (index !== -1) {
remove(index)
}
}
return (
<div>
<button onClick={() => addTodo('New Task')}>Add Task</button>
<ul>
{todos.map((todo) => (
<li key={todo.id}>
<span
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => toggleTodo(todo.id)}
>
{todo.text}
</span>
<button onClick={() => deleteTodo(todo.id)}>Delete</button>
</li>
))}
</ul>
<button onClick={clear}>Clear All</button>
</div>
)
}

Conclusion

Using useArray with TypeScript is a powerful way to manage arrays in your React applications. The added type safety, along with the intuitive array manipulation methods, allows you to write cleaner, more maintainable code. By adopting useArray in your projects, you can reduce bugs, improve code readability, and boost your productivity.

Happy coding with TypeScript and React!