{"id":15465969,"url":"https://github.com/brianegan/reselect_dart","last_synced_at":"2025-04-12T06:21:18.903Z","repository":{"id":53194163,"uuid":"112038549","full_name":"brianegan/reselect_dart","owner":"brianegan","description":"Efficiently derive data from your Redux Store","archived":false,"fork":false,"pushed_at":"2024-05-14T17:31:48.000Z","size":30,"stargazers_count":39,"open_issues_count":2,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-26T01:41:31.347Z","etag":null,"topics":["dart","dartlang","redux","reselect"],"latest_commit_sha":null,"homepage":null,"language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/brianegan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-11-25T22:24:48.000Z","updated_at":"2025-02-17T15:00:58.000Z","dependencies_parsed_at":"2024-10-31T14:04:23.529Z","dependency_job_id":"10f79eb4-ec0a-403d-ad5f-8a7ee55eabb4","html_url":"https://github.com/brianegan/reselect_dart","commit_stats":{"total_commits":12,"total_committers":3,"mean_commits":4.0,"dds":"0.16666666666666663","last_synced_commit":"0b68f292fbcb73e84db45c2b977d22082388ba68"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianegan%2Freselect_dart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianegan%2Freselect_dart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianegan%2Freselect_dart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/brianegan%2Freselect_dart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/brianegan","download_url":"https://codeload.github.com/brianegan/reselect_dart/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248525768,"owners_count":21118749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","dartlang","redux","reselect"],"created_at":"2024-10-02T01:04:26.377Z","updated_at":"2025-04-12T06:21:18.880Z","avatar_url":"https://github.com/brianegan.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Reselect for Dart\n[![Build Status](https://travis-ci.org/brianegan/reselect_dart.svg?branch=master)](https://travis-ci.org/brianegan/reselect_dart) [![codecov](https://codecov.io/gh/brianegan/reselect_dart/branch/master/graph/badge.svg)](https://codecov.io/gh/brianegan/redux_dev_tools)\n\nA Selector library for Dart, based on the original [Reselect JavaScript library](https://github.com/reactjs/reselect#example).\n\n  * Selectors are functions that derive data from a single input, such as a the State of a Redux Store.\n  * Selectors can expose a single point of entry to the data in your [Redux](https://pub.dartlang.org/packages/redux) Store\n  * Selectors can compute derived data, allowing [Redux](https://pub.dartlang.org/packages/redux) to store the minimal possible state.\n  * Selectors are efficient. Derived data is not recomputed unless one of its arguments change.\n  * Selectors are composable. They can be combined to create more complex selectors.\n\nNote: This library and examples are geared toward Redux, but can really work with any Redux implementation or data source.\n\n## Installation\n\nSee the instructions on the package page: https://pub.dartlang.org/packages/reselect#pub-pkg-tab-installing\n\n## Example\n\n### Motivation\n\nLet's set up a simple Redux example to explain our motivation. In our case, we'll have an `AppState` that holds a list of `Products` and the name of our Shop. We want to show a screen that displays a list of all products that are On Sale. We'll also need another page that filters products to those that are less expensive that $10 (or euros or yuan, ya know).\n\nLet's see the simplest way we can do this!\n\n```dart\n// Start with a Product class\nclass Product {\n  String name;\n  bool onSale;\n  double price;\n\n  Product(this.name, this.price, [this.onSale = false]);\n\n  // A helper constructor for creating On Sale items\n  factory Product.onSale(String name, double price) =\u003e new Product(\n    name,\n    price,\n    true,\n  );\n}\n\n// Create a class that holds the State of our app.\nclass AppState {\n  final String shopName;\n  final List\u003cProduct\u003e products;\n\n  AppState(this.shopName, this.products);\n}\n\n// Create your app state at some point in your app\nfinal appState = new AppState(\"Brian's Keyboard Stop\", [\n  new Product(\"Cherry Mx Red switch\", 14.99),\n  new Product(\"Cherry Mx Blue switch\", 9.99),\n  new Product.onSale(\"Cherry Mx Brown switch\", 8.99),\n]);\n\n// Create a function that finds which products are on sale\nfinal selectOnSaleProduct = (AppState state) =\u003e\n    state.products.where((product) =\u003e product.onSale);\n\n// Also create a function that finds the products that are under $10\nfinal affordableProductsSelector = (AppState state) =\u003e\n    state.products.where((product) =\u003e product.price \u003c 10.00);\n```\n\nSo far, so good. Honestly, for small apps, don't go any further than this. Don't import this library, and just write some nice and simple functions. They're great just as they are!\n\n### Create a Single Point of Access\n\nNow, instead of 2 functions that need to derive data from the products, say you had 10-20. This is fine as long as the AppState stays the same, but what if the AppState changes, and the list of Products moves to a different place in the State tree?\n\nThe first improvement we can make is to create a function that \"selects\" the products from your app state, so if any changes are made to the State, one function alone needs to be changed. Then, all of your functions that need the list of products can use this function as a single point of access.\n\nThis example continues from the previous code.\n\n```dart\n// Now, you can write a selector (remember, just a normal function),\n// that finds the products in your AppState. This should be used as the\n// single point of access to the products in your State.\nfinal productsSelector = (AppState state) =\u003e state.products;\n\n// Example usage. This will print the list of products in the AppState.\nprint(productsSelector(appState));\n\n// Now, we'll update our previous selector functions to use this single\n// point of access.\nfinal selectOnSaleProduct = (AppState state) =\u003e\n    productsSelector(state).where((product) =\u003e product.onSale);\n\n// Also create a function that finds the products that are under $10\nfinal affordableProductsSelector = (AppState state) =\u003e\n    productsSelector(state).where((product) =\u003e product.price \u003c 10.00);\n```\n\nYou might notice that we're not doing anything special so far, nor are we\neven using the library! But we have made our code a bit more robust.\n\n### Memoizing Computed Values\n\nNow, let's actually use this library! Selecting part of your State tree can be handy, and requires nothing special, but we can go further. Say you need to derive some data, such as the list of products that are on sale, but to do so is expensive (perhaps there are thousands and thousands of Products in your AppState). In this case, it might be nice to cache (aka memoize) the result and only recompute the result when the inputs change.\n\nThis is exactly what the `createSelector1` to `createSelector10` functions contained in this library are for. These functions combine 1 to 10 selectors together into one new memoized selector.\n\nLet's continue from our previous example.\n\n```dart\n// This example will compute all products that are on sale only when\n// the list of products changes, which it gets from the productsSelector.\nfinal onSaleSelector = createSelector1(\n  productsSelector,\n  (products) =\u003e products.where((product) =\u003e product.onSale),\n);\n\n// Also memoize the function that finds the products that are under $10\nfinal affordableProductsSelector = createSelector1(\n    productsSelector,\n    (products) =\u003e products.where((product) =\u003e product.price \u003c 10.00),\n);\n\n// Example usage. This will print the list of all products on Sale\nprint(onSaleSelector(appState));\n\n// If we try printing again, the onSaleSelector will check if the\n// products have changed, and since they have not, will return the\n// cached result!\nprint(onSaleSelector(appState));\n```\n\n### Combining Selectors\n\nCool, so now we've got a couple of memoized Selectors (functions) that only recompute their values when the underlying data changes! Now let's start putting them together like Legos.\n\nSay you now want to create a \"Snap Deals\" page that shows all on sale products AND all products under $10. We can combine our two memoized selectors from the previous example to create this new selector!\n\n```dart\n// We'll use `createSelector2` to combine 2 selectors with a given function.\n// In this case, we'll combine the onSaleSelector and the\n// affordableProductsSelector from our previous example into a new list\n// containing all of these products.\nfinal snapDealsSelector = createSelector2(\n  onSaleSelector,\n  affordableProductsSelector,\n  (onSaleProducts, affordableProducts) =\u003e\n      ([]..addAll(onSaleProducts)..addAll(affordableProducts)),\n);\n```\n\n### Review\n\nOk, so now that we've covered a decent amount of ground, let's summarize:\n\n  * We've gone from directly accessing the Products contained within the AppState to using a selector (function) that acts as single point of access \n  * Memoized our expensive selectors (using createSelector1) so they don't recompute unless the data they depend on has changed.\n  * Combined those 2 memoized selectors (using createSelector2) into a new memoized selector! How deep can you go!?!?!\n  \nSo that's about it. That's why you'd use this library and the story of how you can create more helpful abstractions along the way, even without including this library!\n\n## Extras \n\n### Changing the memoization\n\nThe default memoize functions come from the [memoize package](https://pub.dartlang.org/packages/memoize). They use the `==` method of your arguments to determine whether or not your input values have changed. Most of the time, properly implementing `==` on your classes is the way to go, but if you need to change how it works, you can supply your own memoize function.\n\nThe following example will use it's own custom memoize function. A memoize function takes in a function and returns a function of the same signature. It is responsible for handling how it caches the resulting values.\n\n```dart\n// This time, we'll write the same onSaleSelector, but provide\n// our own memoize function that will compare the inputs by Identity\n// rather than by using the `==` method!\nfinal onSaleSelector = createSelector1(\n  productsSelector,\n  // This second function is called the combiner. It's the function\n  // that needs to be memoized!\n  (products) =\u003e products.where((product) =\u003e product.onSale),\n\n  // The memoize function takes in the combiner (where all the expensive\n  // computations take place), and returns a new, memoized combiner\n  // function!\n  //\n  // Try not to get scared by the type signature -- the combiner is\n  // simply a function that takes in a list of Products, and returns a\n  // list of Products.\n  memoize: (Func1\u003cList\u003cProduct\u003e, List\u003cProduct\u003e\u003e combiner) {\n    List\u003cProduct\u003e prevProductsArg;\n    List\u003cProduct\u003e prevOnSaleProducts;\n\n    return ((List\u003cProduct\u003e productsArg) {\n      // Use `identical` instead of `==` for comparing arguments\n      if (identical(productsArg, prevProductsArg)) {\n        return prevOnSaleProducts;\n      } else {\n        prevProductsArg = productsArg;\n        prevOnSaleProducts = combiner(arg);\n\n        return prevOnSaleProducts;\n    });\n  },\n);\n```\n\n### Selectors with arguments\n\nTo create a selector which takes arguments, you have a few options. First, you can write a fuction that creates a selector. Let's say you wanted to make the affordable products selector configurable:\n\n```dart\nSelector\u003cAppState, List\u003cProduct\u003e\u003e createAffordableProductsSelector(double price) {\n  return createSelector1(\n    productsSelector,\n      (products) =\u003e products.where((product) =\u003e product.price \u003c price),\n    );\n}\n\n// Usage\nfinal affordableProductsSelector = createAffordableProductsSelector(10.00);\nfinal affordableProducts = affordableProductsSelector(appState);\n```\n\nThe other option: Although a selector only takes in 1 parameter, that parameter can be anything you want. It can be a class that holds multiple values, or a Dart 3 record.\n\n```dart\ntypedef Params = (AppState state, double price);\n\nfinal affordableProductsSelector = createSelector1\u003cParams, (List\u003cProduct\u003e, double), List\u003cProduct\u003e\u003e(\n    (params) =\u003e (productsSelector(params.$1), params.$2),\n    (result) =\u003e result.$1.where((product) =\u003e product.price \u003c result.$2),\n);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianegan%2Freselect_dart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbrianegan%2Freselect_dart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbrianegan%2Freselect_dart/lists"}