Skip to main content

Handling Empty Array Results

To reduce recalculations, use a predefined empty array when array.filter or similar methods result in an empty array.

So you can have a pattern like this:

handling-empty-array-results/firstPattern.ts
import { createSelector } from 'reselect'

export interface RootState {
todos: {
id: number
title: string
description: string
completed: boolean
}[]
}

const EMPTY_ARRAY: [] = []

const selectCompletedTodos = createSelector(
[(state: RootState) => state.todos],
todos => {
const completedTodos = todos.filter(todo => todo.completed === true)
return completedTodos.length === 0 ? EMPTY_ARRAY : completedTodos
},
)

Or to avoid repetition, you can create a wrapper function and reuse it:

handling-empty-array-results/fallbackToEmptyArray.ts
import { createSelector } from 'reselect'
import type { RootState } from './firstPattern'

const EMPTY_ARRAY: [] = []

export const fallbackToEmptyArray = <T>(array: T[]) => {
return array.length === 0 ? EMPTY_ARRAY : array
}

const selectCompletedTodos = createSelector(
[(state: RootState) => state.todos],
todos => {
return fallbackToEmptyArray(todos.filter(todo => todo.completed === true))
},
)

This way if the result function returns an empty array twice in a row, your component will not re-render due to a stable empty array reference:

const completedTodos = selectCompletedTodos(store.getState())

store.dispatch(addTodo())

console.log(completedTodos === selectCompletedTodos(store.getState())) //=> true