createStructuredSelector | Reselect (original) (raw)

A convenience function that simplifies returning an object made up of selector results.

Parameters

Name Description
inputSelectorsObject A key value pair consisting of input selectors.
selectorCreator? A custom selector creator function. It defaults to createSelector.

Returns

A memoized structured selector.

Type Parameters

Name Description
InputSelectorsObject The shape of the input selectors object.
MemoizeFunction The type of the memoize function that is used to create the structured selector. It defaults to lruMemoize.
ArgsMemoizeFunction The type of the of the memoize function that is used to memoize the arguments passed into the generated structured selector. It defaults to lruMemoize.

Examples

Modern Use Case

createStructuredSelector/modernUseCase.ts

import { createSelector, createStructuredSelector } from 'reselect'

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

// This:
export const structuredSelector = createStructuredSelector(
  {
    todos: (state: RootState) => state.todos,
    alerts: (state: RootState) => state.alerts,
    todoById: (state: RootState, id: number) => state.todos[id]
  },
  createSelector
)

// Is essentially the same as this:
export const selector = createSelector(
  [
    (state: RootState) => state.todos,
    (state: RootState) => state.alerts,
    (state: RootState, id: number) => state.todos[id]
  ],
  (todos, alerts, todoById) => {
    return {
      todos,
      alerts,
      todoById
    }
  }
)

In your component:

createStructuredSelector/MyComponent.tsx

import type { RootState } from 'createStructuredSelector/modernUseCase'
import { structuredSelector } from 'createStructuredSelector/modernUseCase'
import type { FC } from 'react'
import { useSelector } from 'react-redux'

interface Props {
  id: number
}

const MyComponent: FC<Props> = ({ id }) => {
  const { todos, alerts, todoById } = useSelector((state: RootState) =>
    structuredSelector(state, id)
  )

  return (
    <div>
      Next to do is:
      <h2>{todoById.title}</h2>
      <p>Description: {todoById.description}</p>
      <ul>
        <h3>All other to dos:</h3>
        {todos.map(todo => (
          <li key={todo.id}>{todo.title}</li>
        ))}
      </ul>
    </div>
  )
}

Simple Use Case

const selectA = state => state.a
const selectB = state => state.b

// The result function in the following selector
// is simply building an object from the input selectors
const structuredSelector = createSelector(selectA, selectB, (a, b) => ({
  a,
  b
}))

const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 }

createStructuredSelector takes an object whose properties are input selectors and returns a structured selector. The structured selector returns an object with the same keys as the inputSelectorsObject argument, but with the selectors replaced with their values.

const selectA = state => state.a
const selectB = state => state.b

const structuredSelector = createStructuredSelector({
  x: selectA,
  y: selectB
})

const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 }

Structured selectors can be nested:

const nestedSelector = createStructuredSelector({
  subA: createStructuredSelector({
    selectorA,
    selectorB
  }),
  subB: createStructuredSelector({
    selectorC,
    selectorD
  })
})

Defining a Pre-Typed createStructuredSelector

As of Reselect 5.1.0, you can create a "pre-typed" version of createStructuredSelector where the state type is predefined. This allows you to set the state type once, eliminating the need to specify it with every createStructuredSelector call.

To do this, you can call createStructuredSelector.withTypes<StateType>():

createStructuredSelector/withTypes.ts

import { createStructuredSelector } from 'reselect'

export interface RootState {
  todos: { id: number; completed: boolean }[]
  alerts: { id: number; read: boolean }[]
}

export const createStructuredAppSelector =
  createStructuredSelector.withTypes<RootState>()

const structuredAppSelector = createStructuredAppSelector({
  // Type of `state` is set to `RootState`, no need to manually set the type
  todos: state => state.todos,
  alerts: state => state.alerts,
  todoById: (state, id: number) => state.todos[id]
})