# memosel

> A library for creating memoized "selector" functions

Latest version **0.0.7** (published 2022-09-09) · ISC license · 0 weekly downloads

## Install

```sh
npm install memosel
pnpm add memosel
yarn add memosel
bun add memosel
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support; pre 1.0.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 0.0.7 |
| Published | 2022-09-09 |
| First published | 2022-06-10 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 37.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | linq2js |
| Maintainers | linq2js |
| Keywords | react, redux, selector, memoized, cache |

## Links

- npm: https://www.npmjs.com/package/memosel
- Repository: https://github.com/linq2js/memosel
- Homepage: https://linq2js.github.io/memosel/
- Issues: https://github.com/linq2js/memosel/issues
- npm.io page: https://npm.io/package/memosel

## Alternatives

- [mobx-react](https://npm.io/package/mobx-react.md) — 2.8M weekly downloads
- [rc-tree](https://npm.io/package/rc-tree.md) — 2.6M weekly downloads
- [@react-oauth/google](https://npm.io/package/@react-oauth/google.md) — 1.3M weekly downloads
- [@wagmi/connectors](https://npm.io/package/@wagmi/connectors.md) — 877.0K weekly downloads
- [vee-validate](https://npm.io/package/vee-validate.md) — 836.4K weekly downloads

## Recent versions

- 0.0.7 (latest) — 2022-09-09
- 0.0.6 — 2022-06-11
- 0.0.5 — 2022-06-10
- 0.0.4 — 2022-06-10
- 0.0.3 — 2022-06-10
- 0.0.2 — 2022-06-10
- 0.0.1 — 2022-06-10

## README

- [`memosel`](#memosel)
  - [Installation](#installation)
  - [Features](#features)
  - [Recipes](#recipes)
    - [Basic Usage](#basic-usage)
    - [Join similar selectors](#join-similar-selectors)
    - [Avoid selector factories](#avoid-selector-factories)
    - [Creating simple memoized function](#creating-simple-memoized-function)
  - [API references](#api-references)

# `memosel`

A library for creating memoized "selector" functions

## Installation

with NPM

```bash
npm i memosel --save
```

with YARN

```bash
yarn add memosel
```

## Features

- Fully Typescript supported
- Multiple keys caching
- Time To Live caching supported
- Structured selector supported

## Recipes

### Basic Usage

```js
import memosel from "memosel";

const selectShopItems = (state) => state.shop.items;
const selectTaxPercent = (state) => state.shop.taxPercent;

const selectSubtotal = memosel()
  // use "input" selector and map it return value to "items" prop of selected value
  .use("items", selectShopItems)
  .build((selected) =>
    selected.items.reduce((subtotal, item) => subtotal + item.value, 0)
  );

const selectTax = memosel()
  .use("subtotal", selectSubtotal)
  .use("taxPercent", selectTaxPercent)
  .build((selected) => selected.subtotal * (selected.taxPercent / 100));

const selectTotal = memosel()
  .use("subttoal", selectSubtotal)
  .use("tax", selectTax)
  .build((selected) => ({ total: selected.subtotal + seleted.tax }));

const exampleState = {
  shop: {
    taxPercent: 8,
    items: [
      { name: "apple", value: 1.2 },
      { name: "orange", value: 0.95 },
    ],
  },
};

console.log(selectSubtotal(exampleState)); // 2.15
console.log(selectTax(exampleState)); // 0.172
console.log(selectTotal(exampleState)); // { total: 2.322 }
```

### Join similar selectors

With reselect

```js
import { createSelector } from reselect;

const getWorldData = state => state.world;

/*
 * Solution 1: one selector for each country
 * Problem: 195 selectors to maintain
 */
const getAfghanistanData = createSelector(
  getWorldData,
  world => extractData(world, 'afghanistan'),
);
// Albania, Algeria, Amer...
const getZimbabweData = createSelector(
  getWorldData,
  world => extractData(world, 'zimbawe'),
);

/*
 * Solution 2: one selector shared by all countries
 * Problem: each call to a different country invalidates
 * the cache of the previous one
 */
const getCountryData = createSelector(
  getWorldData,
  (state, country) => country,
  (world, country) => extractData(world, country),
);

const afghanistan = getCountryData(state, 'afghanistan');
const zimbabwe = getCountryData(state, 'zimbawe');  // Cache invalidated
const afghanistanAgain = getCountryData(state, 'afghanistan');

// afghanistan !== afghanistanAgain

/*
 * Solution 3: use a factory function
 * Problem:
 * - Lost memoization across multiple components
 * - Must call the factory once for each country on each container component
 */
const makeGetCountryData = country => {
  return createSelector(
    getWorldData,
    world => extractData(world, country),
  );
}
```

With re-reselect

```js
import { createCachedSelector } from "re-reselect";

const getWorldData = (state) => state.world;

const getCountryData = createCachedSelector(
  getWorldData,
  (state, country) => country,
  (world, country) => extractData(world, country)
)(
  (state, country) => country // Cache selectors by country name
);

const afghanistan = getCountryData(state, "afghanistan");
const zimbabwe = getCountryData(state, "zimbawe");
const afghanistanAgain = getCountryData(state, "afghanistan");

// No selector factories and memoization is preserved among different components
// afghanistan === afghanistanAgain
```

With memosel

```js
import memosel from "memosel";

const getWorldData = (state) => state.world;
const getCountryData = memosel()
  // create selector factory that use keySelector, the keySelector accepts "country" argument and we use country as the key of selector cache
  // keySelector can accept multiple arguments and the key array can contains multiple items
  .key((country) => [country])
  .use("world", getWorldData)
  // the second and more arguments are the selected keys (they are returned from keySelector of keys())
  .build(({ world }, country) => extractData(world, country));

const afghanistan = getCountryData("vietnam")(state);
const zimbabwe = getCountryData("zimbawe")(state);
const vietnamAgain = getCountryData("vietnam")(state);
// both of vietnam selectors are identical
console.log(getCountryData("vietnam") === getCountryData("vietnam"));
```

### Avoid selector factories

This example shows how re-reselect would solve the scenario described in the [Reselect docs](https://github.com/reduxjs/reselect#sharing-selectors-with-props-across-multiple-component-instances):
how to share a selector across multiple components while passing in props and retaining memoization?

With re-reselect

```js
import { createCachedSelector } from "re-reselect";

const getVisibilityFilter = (state, props) =>
  state.todoLists[props.listId].visibilityFilter;

const getTodos = (state, props) => state.todoLists[props.listId].todos;

const getVisibleTodos = createCachedSelector(
  [getVisibilityFilter, getTodos],
  (visibilityFilter, todos) => {
    switch (visibilityFilter) {
      case "SHOW_COMPLETED":
        return todos.filter((todo) => todo.completed);
      case "SHOW_ACTIVE":
        return todos.filter((todo) => !todo.completed);
      default:
        return todos;
    }
  }
)(
  /*
   * Re-reselect resolver function.
   * Cache/call a new selector for each different "listId"
   */
  (state, props) => props.listId
);

export default getVisibleTodos;
```

With memosel

```js
import memosel from "memosel";

const getVisibilityFilter = (state, listId) =>
  state.todoLists[listId].visibilityFilter;

const getTodos = (state, listId) => state.todoLists[listId].todos;

const getVisibleTodos = memosel()
  // extract listId and use it as selector key
  .key((props) => [props.listId])
  .use("visibilityFilter", getVisibilityFilter)
  .use("todos", getTodos)
  .build(({ visibilityFilter, todos }) => {
    switch (visibilityFilter) {
      case "SHOW_COMPLETED":
        return todos.filter((todo) => todo.completed);
      case "SHOW_ACTIVE":
        return todos.filter((todo) => !todo.completed);
      default:
        return todos;
    }
  });

// usages
const mapStateToProps = (state, props) => {
  return {
    todos: getVisibleTodos(props)(state),
  };
};
```

### Creating simple memoized function

```js
import memosel from "memosel";

const memoizedFunction1 = memosel((x) => ({ result: x * 2 }));
const result1 = memoizedFunction1(1);
const result2 = memoizedFunction1(1);
console.log(result1 === result2); // true

memoizedFunction1(2);
const result3 = memoizedFunction1(1);
console.log(result1 === result3); // false, because memoized function has cache size = 1 by default

// create memoized function with unlimited cache size
const memoizedFunction2 = memosel((x) => ({ result: x * 2 }), { size: 0 });
const result1 = memoizedFunction2(1);
const result2 = memoizedFunction2(2);
const result3 = memoizedFunction2(3);

const result11 = memoizedFunction2(1);
const result22 = memoizedFunction2(2);
const result33 = memoizedFunction2(3);
console.log(result1 === result11); // true
console.log(result2 === result22); // true
console.log(result3 === result33); // true
```

## API references

https://linq2js.github.io/memosel/

---
_Source: https://npm.io/package/memosel · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
