Convert Figma logo to code with AI

reduxjs logoreselect

Selector library for Redux

19,049
674
19,049
39

Top Related Projects

Official React bindings for Redux

27,618

Simple, scalable state management.

48,653

🐻 Bear necessities for state management in React

8,477

The Redux Framework

The official, opinionated, batteries-included toolset for efficient Redux development

16,243

🌱 React and redux based, lightweight and elm-style framework. (Inspired by elm and choo)

Quick Overview

Reselect is a selector library for Redux, designed to efficiently compute derived data from the Redux store. It allows you to create memoized selectors that only recalculate results when the relevant parts of the state tree change, improving performance in React-Redux applications.

Pros

  • Improves performance by memoizing selector results
  • Encourages modular and reusable selector functions
  • Integrates seamlessly with Redux and React-Redux
  • Supports composition of selectors for complex data transformations

Cons

  • Adds complexity to the codebase, especially for smaller applications
  • Requires careful management of selector dependencies
  • May lead to over-optimization if used excessively
  • Learning curve for developers new to the concept of memoization

Code Examples

  1. Creating a basic selector:
import { createSelector } from 'reselect';

const getVisibilityFilter = state => state.visibilityFilter;
const getTodos = state => state.todos;

export const getVisibleTodos = createSelector(
  [getVisibilityFilter, getTodos],
  (visibilityFilter, todos) => {
    switch (visibilityFilter) {
      case 'SHOW_ALL':
        return todos;
      case 'SHOW_COMPLETED':
        return todos.filter(t => t.completed);
      case 'SHOW_ACTIVE':
        return todos.filter(t => !t.completed);
    }
  }
);
  1. Composing selectors:
const getKeyword = state => state.keyword;

export const getVisibleTodosFilteredByKeyword = createSelector(
  [getVisibleTodos, getKeyword],
  (visibleTodos, keyword) =>
    visibleTodos.filter(todo =>
      todo.text.toLowerCase().includes(keyword.toLowerCase())
    )
);
  1. Using selectors with React-Redux:
import { useSelector } from 'react-redux';
import { getVisibleTodosFilteredByKeyword } from './selectors';

const TodoList = () => {
  const filteredTodos = useSelector(getVisibleTodosFilteredByKeyword);
  
  return (
    <ul>
      {filteredTodos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
};

Getting Started

  1. Install Reselect:

    npm install reselect
    
  2. Import and use in your Redux project:

    import { createSelector } from 'reselect';
    
    const mySelector = createSelector(
      [input1, input2],
      (value1, value2) => {
        // Perform calculations and return result
      }
    );
    
    // Use the selector in your components
    const result = useSelector(mySelector);
    

Competitor Comparisons

Official React bindings for Redux

Pros of react-redux

  • Provides official React bindings for Redux, ensuring seamless integration
  • Offers optimized performance with built-in memoization and selective re-rendering
  • Includes hooks like useSelector and useDispatch for easier state management

Cons of react-redux

  • Larger bundle size due to additional React-specific functionality
  • Steeper learning curve for developers new to Redux ecosystem
  • May introduce unnecessary complexity for smaller applications

Code Comparison

react-redux:

import { useSelector, useDispatch } from 'react-redux';

function MyComponent() {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();
  return <button onClick={() => dispatch({ type: 'INCREMENT' })}>{count}</button>;
}

reselect:

import { createSelector } from 'reselect';

const selectCount = state => state.count;
const doubleCount = createSelector(
  selectCount,
  count => count * 2
);

Key Differences

  • react-redux focuses on connecting Redux to React components, while reselect specializes in creating memoized selectors
  • reselect is often used in conjunction with react-redux to optimize selector performance
  • react-redux provides a more comprehensive solution for Redux integration in React applications, whereas reselect is a targeted tool for efficient state derivation
27,618

Simple, scalable state management.

Pros of MobX

  • Simpler and more intuitive API, requiring less boilerplate code
  • Automatic tracking of state changes and re-rendering of components
  • Better performance for complex state updates due to fine-grained reactivity

Cons of MobX

  • Less predictable state updates, as mutations are allowed
  • Steeper learning curve for developers coming from a more functional programming background
  • Potential for overuse of observables, leading to unnecessary re-renders

Code Comparison

MobX:

import { observable, action } from 'mobx';

class Store {
  @observable count = 0;
  @action increment = () => { this.count++; }
}

Reselect:

import { createSelector } from 'reselect';

const selectCount = state => state.count;
const doubleCount = createSelector(
  selectCount,
  count => count * 2
);

MobX uses decorators and mutable state, while Reselect focuses on creating memoized selectors for Redux state. MobX's approach is more concise but less explicit, whereas Reselect promotes a more functional and predictable style of state management.

48,653

🐻 Bear necessities for state management in React

Pros of Zustand

  • Simpler API with less boilerplate code
  • Built-in support for async actions and middleware
  • Smaller bundle size and better performance

Cons of Zustand

  • Less mature ecosystem and community support
  • Fewer advanced features for complex state management
  • Limited integration with React DevTools

Code Comparison

Zustand:

import create from 'zustand'

const useStore = create(set => ({
  count: 0,
  increment: () => set(state => ({ count: state.count + 1 })),
}))

Reselect:

import { createSelector } from 'reselect'

const selectCount = state => state.count
const selectDoubleCount = createSelector(
  selectCount,
  count => count * 2
)

Zustand offers a more straightforward approach to state management, while Reselect focuses on optimizing selector functions for Redux. Zustand's simplicity makes it easier to set up and use, especially for smaller projects. However, Reselect's integration with Redux and its memoization capabilities can be beneficial for larger, more complex applications with performance concerns.

8,477

The Redux Framework

Pros of Rematch

  • Simplified Redux setup with less boilerplate code
  • Built-in effects for handling async actions
  • Automatic code splitting and dynamic model loading

Cons of Rematch

  • Smaller community and ecosystem compared to Redux
  • Learning curve for developers already familiar with Redux
  • May introduce unnecessary abstraction for simpler applications

Code Comparison

Reselect:

import { createSelector } from 'reselect'

const getVisibilityFilter = state => state.visibilityFilter
const getTodos = state => state.todos

export const getVisibleTodos = createSelector(
  [getVisibilityFilter, getTodos],
  (visibilityFilter, todos) => {
    // Filter logic here
  }
)

Rematch:

import { init } from '@rematch/core'

const todos = {
  state: [],
  reducers: {
    add(state, payload) {
      return [...state, payload]
    }
  },
  effects: (dispatch) => ({
    async fetchTodos() {
      const todos = await api.fetchTodos()
      dispatch.todos.add(todos)
    }
  })
}

const store = init({
  models: { todos }
})

Summary

Rematch offers a more streamlined approach to Redux with built-in async handling and simplified setup. However, it may introduce unnecessary complexity for simpler projects and has a smaller community compared to the Redux ecosystem. Reselect, on the other hand, focuses on optimizing selector functions in Redux applications, providing efficient memoization for derived data.

The official, opinionated, batteries-included toolset for efficient Redux development

Pros of Redux Toolkit

  • Provides a comprehensive set of tools for Redux development, including utilities for store setup, creating reducers, and handling immutable updates
  • Includes built-in support for Redux Thunk middleware and Redux DevTools Extension
  • Simplifies common Redux use cases with createSlice and createAsyncThunk

Cons of Redux Toolkit

  • Larger bundle size due to additional features and dependencies
  • Steeper learning curve for developers new to Redux concepts
  • May introduce unnecessary complexity for smaller projects

Code Comparison

Redux Toolkit:

import { createSlice } from '@reduxjs/toolkit'

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: state => state + 1,
  },
})

Reselect:

import { createSelector } from 'reselect'

const selectCounter = state => state.counter
const selectDoubleCounter = createSelector(
  selectCounter,
  counter => counter * 2
)

Key Differences

  • Redux Toolkit focuses on simplifying Redux setup and common patterns, while Reselect specializes in creating memoized selectors
  • Reselect is more lightweight and focused on a single concern, whereas Redux Toolkit provides a broader set of utilities
  • Redux Toolkit can be used alongside Reselect for advanced selector creation and performance optimization
16,243

🌱 React and redux based, lightweight and elm-style framework. (Inspired by elm and choo)

Pros of dva

  • Provides a complete framework for React and Redux, simplifying state management
  • Includes built-in side-effect handling with redux-saga
  • Offers a more opinionated structure, potentially reducing boilerplate code

Cons of dva

  • Less flexible than Reselect for specific memoization needs
  • Steeper learning curve due to its comprehensive nature
  • May be overkill for smaller projects or those not using the full Redux ecosystem

Code Comparison

dva:

import dva from 'dva';

const app = dva();
app.model({
  namespace: 'count',
  state: 0,
  reducers: {
    add(state) { return state + 1; },
  },
});

Reselect:

import { createSelector } from 'reselect';

const getVisibilityFilter = state => state.visibilityFilter;
const getTodos = state => state.todos;

const getVisibleTodos = createSelector(
  [getVisibilityFilter, getTodos],
  (visibilityFilter, todos) => {
    // ... filter logic
  }
);

While dva provides a more comprehensive solution for React and Redux applications, Reselect focuses specifically on efficient state derivation through memoized selectors. dva may be preferable for larger applications seeking an all-in-one solution, while Reselect offers more granular control over performance optimizations in state calculations.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

Reselect

npm packageCoverallsGitHub Workflow StatusTypeScript

A library for creating memoized "selector" functions. Commonly used with Redux, but usable with any plain JS immutable data as well.

  • Selectors can compute derived data, allowing Redux to store the minimal possible state.
  • Selectors are efficient. A selector is not recomputed unless one of its arguments changes.
  • Selectors are composable. They can be used as input to other selectors.

The Redux docs usage page on Deriving Data with Selectors covers the purpose and motivation for selectors, why memoized selectors are useful, typical Reselect usage patterns, and using selectors with React-Redux.

Installation

Redux Toolkit

While Reselect is not exclusive to Redux, it is already included by default in the official Redux Toolkit package - no further installation needed.

import { createSelector } from '@reduxjs/toolkit'

Standalone

For standalone usage, install the reselect package:

# NPM
npm install reselect

# Yarn
yarn add reselect

Documentation

The Reselect docs are available at https://reselect.js.org, and include usage guides and API references:

Basic Usage

Reselect exports a createSelector API, which generates memoized selector functions. createSelector accepts one or more input selectors, which extract values from arguments, and a result function function that receives the extracted values and should return a derived value. If the generated output selector is called multiple times, the output will only be recalculated when the extracted values have changed.

You can play around with the following example in this CodeSandbox:

import { createSelector } from 'reselect'

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

const state: RootState = {
  todos: [
    { id: 0, completed: false },
    { id: 1, completed: true }
  ],
  alerts: [
    { id: 0, read: false },
    { id: 1, read: true }
  ]
}

const selectCompletedTodos = (state: RootState) => {
  console.log('selector ran')
  return state.todos.filter(todo => todo.completed === true)
}

selectCompletedTodos(state) // selector ran
selectCompletedTodos(state) // selector ran
selectCompletedTodos(state) // selector ran

const memoizedSelectCompletedTodos = createSelector(
  [(state: RootState) => state.todos],
  todos => {
    console.log('memoized selector ran')
    return todos.filter(todo => todo.completed === true)
  }
)

memoizedSelectCompletedTodos(state) // memoized selector ran
memoizedSelectCompletedTodos(state)
memoizedSelectCompletedTodos(state)

console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false

console.log(
  memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state)
) //=> true

As you can see from the example above, memoizedSelectCompletedTodos does not run the second or third time, but we still get the same return value as last time.

In addition to skipping unnecessary recalculations, memoizedSelectCompletedTodos returns the existing result reference if there is no recalculation. This is important for libraries like React-Redux or React that often rely on reference equality checks to optimize UI updates.


Terminology

The below example serves as a visual aid:

const outputSelector = createSelector(
  [inputSelector1, inputSelector2, inputSelector3], // synonymous with `dependencies`.
  resultFunc // Result function
)

What's New in 5.0.0?

Version 5.0.0 introduces several new features and improvements:

  • Customization Enhancements:

    • Added the ability to pass an options object to createSelectorCreator, allowing for customized memoize and argsMemoize functions, alongside their respective options (memoizeOptions and argsMemoizeOptions).
    • The createSelector function now supports direct customization of memoize and argsMemoize within its options object.
  • Memoization Functions:

    • Introduced new experimental memoization functions: weakMapMemoize and unstable_autotrackMemoize.
    • Incorporated memoize and argsMemoize into the output selector fields for debugging purposes.
  • TypeScript Support and Performance:

    • Discontinued support for TypeScript versions below 4.7, aligning with modern TypeScript features.
    • Significantly improved TypeScript performance for nesting output selectors. The nesting limit has increased from approximately 8 to around 30 output selectors, greatly reducing the occurrence of the infamous Type instantiation is excessively deep and possibly infinite error.
  • Selector API Enhancements:

    • Removed the second overload of createStructuredSelector due to its susceptibility to runtime errors.
  • Additional Functionalities:

    • Added dependencyRecomputations and resetDependencyRecomputations to the output selector fields. These additions provide greater control and insight over input selectors, complementing the new argsMemoize API.
    • Introduced inputStabilityCheck, a development tool that runs the input selectors twice using the same arguments and triggers a warning If they return differing results for the same call.
    • Introduced identityFunctionCheck, a development tool that checks to see if the result function returns its own input.

These updates aim to enhance flexibility, performance, and developer experience. For detailed usage and examples, refer to the updated documentation sections for each feature.

  • Breaking Changes:

    • Removed ParametricSelector and OutputParametricSelector types. Their functionalities are now integrated into Selector and OutputSelector respectively, which inherently support additional parameters.

License

MIT

References

Click to Expand

Originally inspired by getters in NuclearJS, subscriptions in re-frame and this proposal from speedskater.

NPM DownloadsLast 30 Days