I have a state structure like the following:
{
products: {1: {}, 2: {}, 3: {}},
list: [1, 2, 3],
favorites: [],
}
Basically, that's a Product object map referenced by productID. There's a product list and a list of products that are fav'ed.
In one screen I present the product list with the following selector:
import { createSelector } from 'reselect'
import type { State } from '../../Common/Types/StateType'
const getProductIds = (state: State, props: Object) => state.list
const getProducts = (state: State, props: Object) => state.products
const getProductListSelector = createSelector(
[getProductIds, getProducts],
(ids, products) => {
return ids.map(id => {
if (products[id]) {
return products[id].product
}
})
}
)
export default getProductListSelector
In the favorite screen, I use the following selector:
import { createSelector } from 'reselect'
import type { State } from '../../Common/Types/StateType'
const getProductIds = (state: State, props: Object) => state.favorites
const getProducts = (state: State, props: Object) => state.products
const getFavouritesSelector = createSelector(
[getProductIds, getProducts],
(ids, products) => {
return ids.map(id => {
if (products[id]) {
return products[id].product
}
})
}
)
export default getFavouritesSelector
When I add a favorite, the selector gets computed and displayed correctly. state.favorites returns an array with the fav'ed element and the selector correctly returns an array with the product object. This is an example of the result state after adding a favorite:
{
products: {1: {}, 2: {}, 3: {}},
list: [1, 2, 3],
favorites: [1], // Added '1' as a favorite product
}
However, when I navigate back and forth to my app home (this is a React Native app) and return to the view where both the product list and favorite are displayed, the getFavouritesSelector always return an empty array as the lastResult. This is a wrong value, because there is a favorite in the state, and the selector should return an array with that object, not an empty one. It is worth to notice that getFavouritesSelector selector does not get executed in this case because the lastArgs and args variables are the same (this is correct).
The product list selector, getProductListSelector, gets updated too but it always returns the right lastResult value, no matter how many changes are applied to the state.
Why my getFavouritesSelector is saving the lastResult as an empty array after being updated with values? I can add all three products to the favorite list and my selector will compute the list correctly, but it won't work when I exit and navigate back to the view.
PS: This is how I connect the selector to my component, using react-redux:
export default connect((state: State) => ({
favourites: getFavouritesSelector(state),
}))(MyComponent)
One strange thing to notice is that if I change the favorites selector and use the product selector, both screens will loose the right lastValue when navigated back to them.
Are you mutating favorites?
@HeyImAlex nope, this is what the reducer looks like when I add new products to the list
ADD_PRODUCT_SUCCESS: (state: ProductsState, action: FluxStandardAction) => ({
list: state.products.concat(action.payload.code),
isFetching: false,
error: false,
})
I had a similar issue, but it turns out that in my component, I did mutate my array by mistake.
So in my case my reducer was not mutating my array, but my component code after the selector changed the array, which modified the lastResult inside the memoize. Finally, in my case, the state didn't change for that selector, so the cached lastResult was no longer the same as before.
Thanks @SamuelBeliveau!
Turns out I was using splice inside a common component but without copying the Array, so I was mutating my array in the end. Thanks @HeyImAlex for pointing me to the right direction too!
Thanks @SamuelBeliveau, that was exactly my problem. Saved me hours of debugging.
Most helpful comment
I had a similar issue, but it turns out that in my component, I did mutate my array by mistake.
So in my case my reducer was not mutating my array, but my component code after the selector changed the array, which modified the lastResult inside the memoize. Finally, in my case, the state didn't change for that selector, so the cached lastResult was no longer the same as before.