Coder Social home page Coder Social logo

reselect's People

Contributors

aikoven avatar alex3165 avatar andarist avatar aryaemami59 avatar benshope avatar dependabot[bot] avatar ellbee avatar eskimojo14 avatar examadeus avatar faassen avatar geon avatar heyimalex avatar ianks avatar joshkel avatar lukpsaxo avatar machycek avatar markerikson avatar methuselah96 avatar nickmccurdy avatar npbee avatar pesho avatar phryneas avatar preeternal avatar riddleman avatar ryanatkn avatar simenb avatar spaintrain avatar speedskater avatar threehams avatar wbreakell avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reselect's Issues

memoize map/fmap

A common pattern in my app state is computed properties on dictionary elements. E.g. building an index of data that is identified by a key.

App state might look like

{ '1234-0001': arr1, '1234-0002': arr2, ... }

and the selector returns something like

{'1234-0001': makeExpensiveIndex(arr1), '1234-0002': makeExpensiveIndex(arr2), ... }

which might be done like _.mapObject(state, makeExpensiveIndex). You don't want to recompute the index on arr2 if only arr1 has changed.

At first glance it looks like reselect can't manage this case. Is there a simple way to do it? Or a different way of modeling dynamic data?

Similar patterns can happen with arrays, but it's harder to handle since we can't hash on object values in javascript. You have to either cache by index (position in the array), or by some unique property on the elements in the array (like the key prop in react element collections).

Dependency recalculation with arrays

I'm hitting an annoyance with memoization of arrays.

I'm keeping normalized data in a redux store as recommended (objects indexed by id, with a separate array of indexes for ordering), and using a selector to denormalize these into an array of objects.

So I'm doing this, effectively:

const selectObjectsById = (state) => state.objectsById;
const selectIds = (state) => state.ids;

const selectDenorm = createSelector(
    selectObjectsById,
    selectIds,
    (objectsById, ids) => ids.map(id => objectsById[id]),
);

When state.objectsById changes, selectDenorm will recalculate. But if state.objectsById changes in a way that doesn't change the result of selectDenorm (for example, if a new object is added to state.objectsById whose id isn't in state.ids) then the new array calculated is memberwise identical to the old one but is not reference-equal.

This means a downstream selector is needlessly recalculated. For example:

const selectMain = createSelector(
    selectDenorm,
    selectFoo,
    selectBar,
    (denorm, foo, bar) => ({ denorm, foo, bar }),
);

I can use createSelectorCreator with a custom equality checker to make a custom selector for selectMain to stop this needless recalculation. But if then selectFoo changes (according to that equality checker), selectMain is recalculated -- using the new selectDenorm result. Which means selectMain(state).denorm is a different reference, even though my custom equality checker insists it's the same.

(At least, this is my understanding of what's going on, which might be wrong.)

If reselect, when it recalculates a selector, uses the previous value of each argument that passed the equality check, then this would improve things. Or perhaps it could run the equality check on the result of the recalculation against the previous result, and return the previous one if they're the same. Or do both?

Or should I be doing things differently?

Thanks for a great library!

Can i use redux actions in reselect?

Maybe this is very stupid question but, this is what i mean.
I would like to make shopping cart, so i have state that contains all shop items, lets call it shop_items,
this comes from reducer.
Then i have a reselect , that sends off cart_items, and it gets data from shop_items, if someone clicks "Add to cart" button.
So i need to add redux-actions to filter shop_items by action.id and return data. But i cant figure it out how..

Memoization works too well with composed selectors

It seems that composed selectors create unpredictable updates when composed. Or, rather, my views don't update when I update data in my store that is memoized more than once through the composition chain of selectors.

For example:

let selector1 = createSelector(/*...*/)
let selector2 = createSelector(
  [
    selector1,
    state => state.something
  ],
  (selector1Stuff, something) => {

    return {
      selector1Stuff,
      something
    };
  }
);

If data in selector1 should be updated, it should trigger the update or anything composing with it.

Can this be used to group items?

Lets say this is my state:

{ 
  hotels: [], 
  offices: [],
  stores: [],
  typeFilter: ['hotels', 'stores'] 
}

can reselect be used to select a combined list of items out of ยดhotels, offices and storesยด based on the types specified in typeFilter and and that match a keyword search, sorted and grouped by distance?
Given that for each item a distance to the users position can be calculated the final list would include cities and stores:

groupedFilteredItems: {
  near: [
    {
      type: "store",
      distance: "10",
    }
  ],
  inWalkingDistance: [
    {
      type: "hotel",
      distance: "1000",      
    }
  ],
  far: [
   {
      type: "store",
      distance: "4000"
   }
  ]
}

a release script to tag on github, npm publish

When we do a release we should both tag the release in git as well as do an npm publish, perhaps update the version number in the package.json too to be the next release. Is there some kind of script out there that can help us do this? How does this interact with writing release documentation on github? And how does that interact with the changelog? See also issue #14

This was pointed out by @wyze

Possibility of implementing setters

Part of the power of getter libraries like Haskell's Lens is that they also include a powerful mechanism for setters as well.

As an outside observer who is in no way an expert on Redux or Reselect, I wonder if it's possible to implement setters in Reselect.

Here's what I dreamed up:

const postSelector = state => state.posts;
const titleSelector = createSelector(
    [postSelector],
    posts => posts.reduce((acc, post) => acc.push({id: post.id, title: post.title}), [])
);

const titleCaseSetter = createSetter(
    [UPDATE_POST, MARK_POST],
    [titleSelector],
    titles => titles.map(title => title.title.toUpperCase())
);

Where createSetter is something like this:

function createSetter( actions, selectors, setterFunction )
{
    if( !Array.isArray( actions ) )
        actions = [actions];

    var setterEval = setterFunction(...selectors);

    for( let i = 0; i < actions.length; i++ )
    {
        dispatch({
            type: actions[i],
            ...setterEval
        });
    }
}

Thoughts? I'm not sure if it's even possible but it seemed like a cool idea.

Add a way to shortcut evaluation

Just an idea for dealing with situations where a selector function should be conditional.

Here's a contrived example: Imagine you have a huge list and a toggle button to do some complex filter calculations on the list. Your base selectors look like this.

const hugeList$ = state => state.hugeList;
const shouldFilter$ = state => state.shouldFilter;

One way to handle this is by making a filteredList$ selector that depends on hugeList$.

// Method #1

const filteredList$ = createSelector(
  [hugeList$],
  (hugeList) => {
    /* do expensive computation here */
    return filteredList;
  }
);

const finalList$ = createSelector(
    shouldFilter$, filteredList$, hugeList$],
    (shouldFilter, filteredList, hugeList) => {
        return shouldFilter ? filteredList : hugeList;
    }
);

The issue is that filteredList is computed even if shouldFilter is false. If hugeList changed very often but shouldFilter was usually false, you'd be doing a lot of unnecessary re-calculation.

To avoid that you could move the shouldFilter check into filteredList$;

// Method #2

const filteredList$ = createSelector(
  [shouldFilter$, hugeList$],
  (shouldFilter, hugeList) => {
    if (!shouldFilter) { return null; }
    /* do expensive computation here */
    return filteredList;
  }
);

const finalList$ = createSelector(
    [filteredList$, hugeList$],
    (filteredList, hugeList) => {
        return filteredList === null ? hugeList : filteredList;
    }
);

But now the problem is that you recompute every time shouldFilter is toggled. If someone repeatedly clicks the toggle button the UI is going to start lagging hard.

The crux of the issue is that we have no way to conditionally execute a selector. We want to compute a value under certain circumstances only, but using memoize we can't shortcut the function without expiring our cached value.

Some potential solutions:

  • A Symbol that can be returned to shortcut execution directly. The wrapping function would check the return value and return the cached value if it was the symbol. In the example you could just take the second method and return the special symbol instead of null.
  • Something like shouldSelectorUpdate that passes the last used dependency values and the current dependency values and lets the user decide if they wanted to recompute.

The only issue I see now is the need for a default value. As this situation only comes up times that you don't actually want to use the value, it seems safe to just default to null and let the user override if they need to.

lodash merge vs object.assign in redux

So I had a reducer like:

export default function entities(state = initialState, action) {
  if(action.response && action.response.entities) {
    return {
      ...state,
      ...action.response.entities
    }
  }

  return state;
}

And some selectors:

export const cardIdSelector = createSelector(
  [
    state => state.cards.cards,
    state => state.entities.cards
  ], (cardIds, cardsById) => cardIds.map(cardId => cardsById[cardId])
);

export const cardSetSelector = createSelector(
  [
    cardIdSelector,
    state => state.entities.sets
  ], (cards, sets) => cards.map(card => ({
    ...card,
    set_id: sets[card.set_id]
  }))
);

I was getting Unhandled promise rejection TypeError: Cannot read property 'set_id' of undefined when I updated state.cards.cards (it's an array of ids. I'm certain it's not mutated in the reducer that updates the value either). It wasn't working until I change my entities reducer to use Lodash's merge instead of spread operators.

Is this a bug or is there a reason I have to use lodash/merge?

createSelector fail silently ?

Hi, I just started to use Reselect and found it seems to fail silently when call createSelector.

Example

const tickAssetFilter = (assets, contracts) => {
    console.log('f', assets.filter);        // undefined
    const tickAssets = assets.filter(a => {
    ......


createSelector(
    fullTradesSelector,
    trade => {
        const tickAssets = tickAssetFilter(trade.assets, trade.contracts);
        const cloneTrade = trade;
        cloneTrade.assets = tickAssets;
        return cloneTrade;
    }
);

assets.filter does not exists, I would expect

Uncaught TypeError: s.filter is not a function(โ€ฆ)

but nothing is shown instead, and the Component not construct at all

Is this a design decision or a bug?

Return a calculated value instead of an object with a calculated value

Hi,

Is it allowed to create an exposed selector which returns a calculated value, instead of the calculated value wrapped inside an object?

e.g.

// this selector will return a calculated value, istead of `{ key: calculatedValue }`
export const optionsArraySelector = createSelector(
  (filter) => Object.keys(filter.options).map(id => options[id])
)


@connect(
  state => ({
    // now we're able to define the prop's name
    options: optionsArraySelector(state.filter)
  })
)
export default class ...

Umbrella 1.0

  • Variadic Dependencies #27
  • Pass props into selectors #20
  • Way to shortcut evaluation #7
  • Customizable memoize function
  • Update Documentation #22, #34, #26
  • Lint task

Discussion here: #35

Selector with props does not make sense when defaultMemoize has cache size = 1

Assumption:

  • A component that appears on a page multiple times
  • The component use ReactRedux.connect and reselect to select data based on state and component.props

Problem:
If the page have two or more component instances and their props are different, the memoize function (which has cache size = 1) will always cache miss.

This is because the memoize function is shared amongst all component instance, thus, component with different props will always invalidate each other.

Solution:
There are multiple solutions to this issue. I think memoize cache should bound to the component instance.

Using Reselect in react-redux 0.5+ @connect decorator

Hi all,
In react-redux new @connect API we don't have access to the (parent) props, which resembles Reselect's API.
To further filter state with props as parameters@connect allows to take control over the merging of props in it's third parameter 'mergeProps'. https://github.com/gaearon/react-redux#arguments and also see this PR from @jhollingworth: reduxjs/react-redux#36

Is the mergeProps function also a good place to make use of the composing/memoizing properties of Reselect?

As (pseudo) example:

export const dataSelectorFactory = (path) => {
    return immutableCreateSelector(
        [state => state],
        data => {
            return { value: data.getIn(path) };
        }
    );
};

@connect(
    state => state.data,
    null,
    (mergeProps, dispatchProps, parentProps) => {
        return { dataSelectorFactory(parentProps.path)(mergeProps), ...parentProps }
    }
)
class User extends Component {
   ...
}

Or am I overthinking this and is there no use-case for Reselect here?
Thanks in advance!!

RxJS Based Selector

Anyone have any thoughts on how to implement a RxJS based selector?

const shopItemsSelector = onState => onState
  .pluck('shop')
  .pluck('items')
  .distinctUntilChanged()

const taxPercentSelector = onState => onState
  .pluck('shop')
  .pluck('taxPercent')
  .distinctUntilChanged()

const subtotalSelector = shopItemsSelector
  .map(items => items.reduce((acc, item) => acc + item.value, 0))

const taxSelector = Rx.combineLatest(
  subtotalSelector, 
  taxPercentSelector, 
  (subtotal, taxPercent) => subtotal * (taxPercent / 100))

export const totalSelector = Rx.combineLatest(
  subtotalSelector,
  taxSelector,
  (subtotal, tax) => { return {total: subtotal + tax }})

I think it is very similar to how reselect works today but allows a lot more flexibility. The main issue I have is how make this compatible with reselect and/or react-redux.

Document per-component memoization with React Redux

React Redux added per-instance memoization in 4.3.0.

This approach, originally explained here (although the API changed), allows significant perf wins for Reselect when computed state props depend on componentโ€™s own props, as those are now memoized per instance.

I think this approach is worth explaining both here and in the Computing Derived Data recipe. I donโ€™t have time to contribute this, but Iโ€™m sure it will be a very valuable addition.

cc @tgriesser who implemented the feature in React Redux

Multiple @connect decorators?

Just asking, not sure about the best practice here.
Say a component deep in the tree needs a derived or composed state, all the "main" stores are declared on the root component... is it considered good practice to add another @connect on the deep component, or should we pass the derived state down from the root component too?

Feature request: prop values selector

A common pattern I find when composing selectors is to do something like this:

var myItem = createSelector(
  itemOne,
  itemTwo,
  itemThree,
  (itemOne, itemTwo, itemThree) => ({itemOne, itemTwo, itemThree})
)

It'd be a little cleaner if we could do:

var myItem = createSelector({itemOne, itemTwo, itemThree})

Where reselect would recognize the first arg is an object, pick the values off and spread those into the arguments, and then remap the result values of these functions to the keys.

var myItem = createSelector({
  keyOne: selectorForSome('value'),
  keyTwo
})
myItem(state) === {keyOne: value, keyTwo: value}

If this seems outside the scope of the lib, I can write it as a plugin, just thought I'd bring it up as a common pattern I've found useful.

join rackt?

While I'm happy to host this on my github page, I don't want to get more credit for this than I should get. What do people think about hosting it under rackt, like Redux?

Allow variadic dependencies as well as an array

As well as the current system of createSelector([a, b, c], (a, b, c) => ...) it would be great if it would also be possible to do createSelector(a, b, c, (a, b, c) => ...)

This would allow us to implement robust and strictly typed Typescript definitions on createSelector.

Documentation Improvements

Things to add to the documentation:

  • How to use a Factory function to create selectors with parameters
  • Document usage of the exported memoize function (if we add it)
  • Explain how default memoize works and its limitations
  • Using Reselect without Redux
  • When you might want to use a different valueEquals function
  • Rules of thumb when creating selectors
  • Unit testing selectors (#34)

Anything else?

Middleware for debugging?

Hi there team. Really enjoy the library.

Was wondering if there's been any consideration thus far for introspection into one's selectors via middleware or some other modification? As apps grow bigger and selectors increase in complexity, I can see a lot of value in having some way to quickly (and visually) trace the origination of an arbitrary selector's value.

All selectors created by createSelector can be described as having X 'ancestors' and Y 'descendants'. Seems to me these relationships lend themselves really well to being represented as a visual hierarchy; a tree, graph, or what have you.

Consider the example in the README:

import { createSelector } from 'reselect'

const shopItemsSelector = state => state.shop.items
const taxPercentSelector = state => state.shop.taxPercent

const subtotalSelector = createSelector(
  shopItemsSelector,
  items => items.reduce((acc, item) => acc + item.value, 0)
)

const taxSelector = createSelector(
  subtotalSelector,
  taxPercentSelector,
  (subtotal, taxPercent) => subtotal * (taxPercent / 100)
)

export const totalSelector = createSelector(
  subtotalSelector,
  taxSelector,
  (subtotal, tax) => ({ total: subtotal + tax })
)

One could conceive of a graph showing the flow of state down through to totalSelector:
image

Nodes on such a graph could contain information such as the number of re-computations on that selector, the last memoized value, when the value last changed, etc.

Curious to know if there's interest in a project that would generate output like the above, or if someone's done similar work already. Either way, I'd love to help out!

Keep /src included in package.json

For folks who would like to install a specific version of the library from npm (I just tried installing the 1.0 alpha), since the /lib dir isn't compiled there doesn't seem to be anything to reference in the node_modules after it pulls from github.

Also worth considering, support the jsnext:main field in package.json so we can use tooling to target the ES6.

Add documentation about how to test

It would be really useful to have a section in the readme with an example of how to write unit tests for selectors. The obvious strategy is to have two functions for every selector; the selector and the pure function:

export const getAddition = (a, b) => a + b // test this one
export const additionSelector = createSelector([ aSelector, bSelector ], getAddition);

However, this can be quite annoying because (at least in my app) the pure functions are almost always only used within the selector so it means having to figure out a naming strategy, be extra careful for typos, have a bigger API footprint, etc. However, if testing the selectors directly are we going to run into subtle problems with memoization or anything like that? Does testing selectors directly even work as a concept?

Any advice appreciated!

Explain memoization logic

In the documentation it says:

Selectors are efficient. A selector is not recomputed unless one of its arguments change.

In the following example, I test memorisation by running testMemoizeSelector twice, which is generating a random number as an outcome. It works.

Then I add a new member to the data.screenings, which affects the outcome of one of the child selectors. However, it does not discard the testMemoizeSelector cache, which it should based on the description:

curiosity:2015 08 04 reselect gajus$ babel-node ./test
screenings.length 8
testMemoizeSelector #0 0.8094113706611097
testMemoizeSelector #1 0.8094113706611097
testMemoizeSelector #2 0.8094113706611097
import {
    createSelector
} from 'reselect';

import _ from 'lodash';

let data = {};

data.locations = [
    {
        id: 1,
        name: 'Foo',
        address: 'Foo st.',
    },
    {
        id: 2,
        name: 'Bar',
        address: 'Bar st.',
    }
];

data.screenings = [
    {
        locationId: 1,
        id: 1,
        timestamp: 1438423200000
    },
    {
        locationId: 1,
        id: 2,
        timestamp: 1438509600000
    },
    {
        locationId: 1,
        id: 3,
        timestamp: 1438527600000
    },
    {
        locationId: 1,
        id: 4,
        timestamp: 1438598600000
    },
    {
        locationId: 2,
        id: 1,
        timestamp: 1438423200000
    },
    {
        locationId: 2,
        id: 2,
        timestamp: 1438509600000
    },
    {
        locationId: 2,
        id: 3,
        timestamp: 1438527600000
    },
    {
        locationId: 2,
        id: 4,
        timestamp: 1438698600000
    }
];

let locationSelector = state => state.locations,
    screeningSelector = state => state.screenings,
    testMemoizeSelector;

testMemoizeSelector = createSelector(
    [
        locationSelector,
        screeningSelector
    ],
    (locations, screenings) => {
        console.log('screenings.length', screenings.length);

        return Math.random();
    }
);

console.log('testMemoizeSelector #0', testMemoizeSelector(data));

console.log('testMemoizeSelector #1', testMemoizeSelector(data));

data.screenings.push({
    locationId: 2,
    id: 4,
    timestamp: 1438698600001
});

console.log('testMemoizeSelector #2', testMemoizeSelector(data));

Selector parameter from React Router

Hi, I try to use create a selector with the param from React Router for a Master > Detail application, but it looks like it recalculate it every I go into the Detail-view. Should not this be cached?

(when I return to the Masterview, the id-param of the router will be undefined, but it should equal when I re-enter the Detail-view (and viewing an item with the same ID)

import {createSelector} from 'reselect'

const getRouterID = (state) => state.router.params.id
const getItems = (state) => state.items // array

export const getSelectedBook = createSelector(
  [getRouterID, getItems],
  (id, items) => {
    return items.filter((item) => {
      return item.id === id
  }
)

Can you pass an argument to a selector?

I have a component that computes derived data from a store, but based on a variable argument. Can I use reselect for this?

For example I would like to move the following method to a selector. It merges the options and current values from a filter store based on the group argument:

getOptionsWithValues (group) {
    const { options, values } = this.props.filter
    const opts = options.filter(opt => opt.group === group)
    return opts.map(opt => ({ ...opt, value: values.get(opt.id) }))
  }

I feel like I should move this computation outside of the component. In a traditional Flux implementation I would create this method as a getter on the store but I don't know how to approach this with Redux.

maintain a changelog

We should maintain some kind of changelog so people can see what's new in releases. I usually just maintain a CHANGES document and make sure to update it each time there's a change, but perhaps there are other ways -- what does Redux do, for instance?

Writing Tests - Stubbing composed selectors

Currently we have some complex logic around state tested in one selector, all is good there:

const complexSelector = createSelector(
   someSimpleSelectors,
   (a,b,c) => // does complex logic and returns one of a few simple values
)

The tests for this are good, we have many permutations of state fed in, and a few simple returned values.

This complex selector is then re-used by other selectors!

const otherSelector = createSelector(
   selectors.complexSelector, selectors.anotherSelector,
   (complexResult, anotherValue) => // uses the simplified values from complex state
)

Instead of having to put all of the permutations of state into otherSelector (that we've already tested), we were hoping to stub out complexSelector for this test. This would allow us to mock the simple return values of complexSelector when testing otherSelector!

One problem here is that when we sinon.stub(selectors, 'complexSelector'), it seems to not be the same reference to the complexSelector function that is used by otherSelector!

We've tried wrapping all the selectors in an object and exporting that, but same deal... It's like createSelector is actually creating new references to the select functions you give it... making them un-stubbable (is that a word?)...

Our only solution to this so far has been to just resort to simple state select functions, like so:

const otherSelector = (state) => {
   const complexResult = selectors.complexSelector(state);
   const anotherValue = selectors.anotherSelector(state);
   // uses the simplified values from complex state
}

And then stubbing works fine.

This feels like going backwards though... is there any way to get the stubbing to work for selectors given to createSelector?

Maybe a PR to expose the dependendeded on selector functions when using createSelector, so that they can be stubbed?

Compose selectors with additional arguments

Hi there, is it possible to compose selectors with arguments? If have a simple selector which returns a selection, another selector which takes a nested collection (key is passed as a argument) and another selection which consumes the selection:

import createSelector from '../createSelector';

// selection.js: Returns a map of selections: {availability: [], user: []}
export default createSelector(
  [state => state.ui.selection],
  selection => selection
);
import createSelector from '../createSelector';
import selectionSelector from './selection';

// selected.js: Returns a specific selection from the map (e. g. "user")
export default createSelector(
  [selectionSelector],
  (selection, bucket) => selection && selection.hasOwnProperty(bucket) ? selection[bucket] : []
);
import Immutable from 'immutable';
import selectedSelector from '../ui/selected';
import {createImmutableSelector} from '../createSelector';

// selectedUsers.js: Returns a collection of selected users
export default createImmutableSelector(
  [
    state => state.users,
    selectedSelector('user') // This does not work
  ],
  (users, selection) => users.reduce((result, user) => {
    // Select only the users which are selected
  }, Immutable.List())
);

How to apply a selector with others in the connect decorator?

From the documentation I can't seem to figure out how to combine my selector with the other connect parameters.

This is my selector

import { createSelector } from 'reselect'

const optionsSelector = state => state.options
const valuesSelector = state => state.values
const currentGroupSelector = state => state.currentGroup

export const currentOptionsWithValuesSelector = createSelector(
  [optionsSelector, valuesSelector, currentGroupSelector],
  (options, values, group) => {
    const opts = options.filter(opt => opt.group === group)
    // create an array of new option object where value is merged in
    const result = opts.map((opt) => ({ ...opt, value: values.get(opt.id) }))
    return {currentOptionsWithValues: result}
  }
)

Now I would like to pass both the state filter data and the selector result to my component. I tried to make it work like this:

@connect(
  state => (Object.assign({
    filter: state.filter
  }, currentOptionsWithValuesSelector(state.filter)))
)
export default class MyComponent extends Component {

}

Apart from that this would be ugly and hard to read it doesn't seem to work either. How can I add the selector to the connect (in an elegant way)?

How-to: use Reselect with props/state

Reselect is good for selecting "static" data. But the selection result cannot be always deduced from store alone. Sometimes we could need more information from props or state.

In a master/detail component set, instead of using store, the master could use props to send the ID to the detail component.

It would be great if Reselect could jump in and select data based on store + props.

test coverage tool

Does anyone know a good test coverage tool? This way we can make sure we have full coverage of our code.

Selectors do not hot reload

With webpack hot reloading, when fixing a bug inside a selector, the React view does not reflect the change after hot reloading.

Any reason for this?

Maybe the memoized values should be trashed on reload?
Or maybe it's react-redux connect that should reconnect on selectors updates?

How to combine with a non dependent state in Redux?

I have the following selector:

export const selector = createSelector(
    (state) => state,
    (state) =>  {
        return { 
                computedState: state.A + state.B
         }
    }

When I connect with connect(selector) I get computedState in my props.

However I have another state that I would like to use in my props, let's call it state.C

How do I access state.C in my props? If I change my selector to the below, does that mean changes in state.C will recompute my computedState props? If so, then I shouldn't put state.C in my selector. If that is the case, how do I connect state.C into my connect function for redux? If the below is the correct way of including stateC please confirm that it won't recompute when stateC changes. Thanks!

export const selector = createSelector(
    (state) => state,
    (state) =>  {
        return { 
                computedState: state.A + state.B,
                state.C
         }
    }

Composing two selectors does not work?

I have the following two selectors:

export const matchingRegionsSelector = reselect.createSelector(
  [authorizedRegionsSelector, state => state.regions.regionMap, state => state.regions.filter],
  (regionIds, regionMap: RegionMap, filter: string) => filter
    ? _.filter(regionIds, (rId:string) => regionMatches(regionMap[rId], filter))
    : regionIds);
export const visibleRegionsSelector = reselect.createSelector(
    [matchingRegionsSelector, state => state.regions.regionMap],
    (matchingRegionIds, regionMap) => {
        let visibleRegionIds = [];
        _.forEach(matchingRegionIds, id => addHierarchy(id, regionMap, visibleRegionIds));
        return visibleRegionIds;
    });

Unfortunately it appears that only matchingRegionsSelector is re-evaluated when filter changes. And even though the result of matchingRegionsSelector changes, visibleRegionsSelector is not re-evaluated.

I had to add the filter property to get it working:

export const visibleRegionsSelector = reselect.createSelector(
    [matchingRegionsSelector, state => state.regions.regionMap, state => state.regions.filter],
    (matchingRegionIds, regionMap, filter) => {
        let visibleRegionIds = [];
        _.forEach(matchingRegionIds, id => addHierarchy(id, regionMap, visibleRegionIds));
        return visibleRegionIds;
    });

Any thoughts?

how to transform nested data without unnecessarily creating new object references

say I have this structure:

categoryById {
    cat1: {name:..., products: [1,2,3]}
    ....
}
productsById {
    prod1: {id, name:....}
}

if I want a selector that populates categories.products I could do something like:

categoriesSelector = createSelector(
[categoryByIdSelector, productsByIdSelector],
(cats, prods) => {
       var newCats = [];
       cats.forEach((cat) => {
            var newCat = {...cat};
            var newProds = [];
            cat.produts.forEach((prodId) => {
                    prods.push(prods[prodId]);
            }
            newCat.products = newProds;
            newCats.push(newCat);
       }
       return newCats;
}); 

However when I update a product, I rebuild the entire categories object, which gives me new object references and seems like it would break pure component optimization. What I want is to only create new references for the modified product. Maybe there's a way of nesting parameterized selectors that would do this, but I haven't been able to work out how that should look.

Pass state as argument to resultFunc.

My code has reducers that expose "getter" functions that understand the internal structure of their state. I'm using Immutable to store this state.

It's handy to have the getters as they can internalize certain knowledge about the state - for example, ensuring Map entries are always accessed via a Number() call (MyState.someMap.get(Number(id))), or throwing if an invalid key is requested.

These exported getter functions receive the store's root state, as they are typically called from a react-redux connect().

Therefore, I'd like to receive the state into my resultFunc, instead of (or in addition to) the mapped dependencies, so I can continue to use my getters. This would probably best implemented as an alternative to createSelectorCreator.

I can make this change, but wondered if anyone else had any thoughts or objections to this approach.

The objections I've thought of are that it prevents the current convenient selector composition (assuming only the state is passed to the result func), and it would be easy to accidentally make your selector depend on sections of the state that aren't included in your dependencies.

I include an example just in case my explanation wasn't clear.

// model.js    
const Group = Record({ id: null, type: null })
const User = Record({ id: null, defaultGroupId: null })

// GroupStateReducer.js
export default function GroupState(domain, action) { 
    if (domain == undefined) domain = Map(); // indexed groups by Number(group.id)      
    // rest of reducer 
}
export function getGroup(id, state) { 
    return state.GroupState.get(Number(id)) || throw new Error('Unknown group'); 
}

// UserStateReducer.js
export default function UserState(domain, action) { 
    if (domain == undefined) domain = Map(); // indexed users by Number(user.id)      
    // rest of reducer 
}
export const getAllUsers = (state) => state.UserState;
export function getUser(id, state) { 
    return state.UserState.get(Number(id)) || throw new Error('Unknown user'); 
}

// selectors.js

// with reselect as it stands, would be:
export const getGroupUserTypesCurrent = createSelector(
    (state) => state.UserState,
    (state) => state.GroupState,
    // good bye encapsulation
    (userState, groupState) => userState.map((user) => user.id + " default group type " + groupState.get(Number(user.defaultGroupId)).type
)

// I'd like to replace with:
// "createStateReceivingSelector" - naming things... caching things... etc.
export const getGroupUserTypes = createStateReceivingSelector(
    (state) => state.UserState,
    (state) => state.GroupState,
    (state) => getAllUsers(state).map((user) => user.id + " default group type " + getGroup(user.defaultGroupId, state).type
)

Memoizing Hierarchial Selectors

I'm a bit stuck on how one would go about memorising the following selector using this library:

export function selectItem(items, id) {
  const item = items[id]
  return {
    id,
    ...item && {
      children: item.children.map((id) => selectItem(items, id))
    }
  }
}

Currently if any item in the items state is modified all of the selections need to be rebuilt.

Any suggestions on how to go about with these kind of scenarios?

A more complete example: https://gist.github.com/ronag/bc8b9a33da172520e123

keypaths

We could implement key path arrays as an argument to createSelector as a shortcut, i.e instead of this:

createSelector([state => state.a.b], value => value);

we could allow this:

createSelector([['a', 'b']], value=>value);

Or alternatively a string spelling:

createSelector(['a.b'], value=value);

to get an object.

We should make how to interpret a key path pluggable in createSelectorCreator. This way application authors can deal with special cases where state is a ES6 Map or an Immutable.js object, or whatever else.

We could even interpret anything that's not a function as a "keypath" and pass it along to the key path resolver function, in which case using an array or a dotted name is up to the application author.

Explain that reselect does not depend on Redux

I have the need to use the concept of memoized selectors on an app using Facebook's Flux and as I'm following Redux and satellite projects I heard of reselect and find it to be a useful solution.

Maybe it would be nice if you explain that you do not need Redux to use reselect. What you guys think?

Howto: Use reselect at component-level without connect

In your docs, it is only mentioned how to use reselect at the connect-level, but this wouldn't work with some parts of our application, what we needed was some way to apply functions to the props of the components, and it was actually quite easy to do this with reselect:

I created a function to wrap components with, called select:

let React = require('react');
let _     = require('lodash');

let select = (...selectors) => {
    return (WrappedComponent) => {

        let NewWrappedComponent = React.createClass({
            render: function () {
                let appliedSelectors = selectors.map(fn => {
                    return fn(this.props);
                });
                let calculatedProps = _.merge(...appliedSelectors);
                return <WrappedComponent {...this.props} {...calculatedProps} />;

            }
        });
        return NewWrappedComponent;
    };
};

module.exports = select;

With select you can easily use selectors at Component-level:

let { createSelector } = require('reselect');
let React = require('react');

let shopItemsSelector = state => state.shop.items
let taxPercentSelector = state => state.shop.taxPercent

const subtotalSelector = createSelector(
  shopItemsSelector,
  items => items.reduce((acc, item) => acc + item.value, 0)
)

let Component = select(subTotalSelector)(React.createClass({...}));

I've just build it, so it's really simple at the moment, but it works really good for me, hot-reloading worked out of the box.

I just wanted to share, because someone might have the same use-case as I did.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.