Reselect: Usage of reselect, hooks and lodash.get

Created on 11 Jul 2019  路  4Comments  路  Source: reduxjs/reselect

Hi there,

We have a big app in which we're gradually replacing old stuff with better stuff.
We're currently replacing some mapStateToProps with hooks and reselect.

Would this code break anything in the memoization?

import { createSelector, createStructuredSelector } from 'reselect';
import { get } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { useEffect } from 'react';


const structuredSelector = createStructuredSelector({
    data1 : state => get(state,'some.deep.possibly.null.values'),
    data2 : state => Object.keys(get(state,'some.deep.possibly.null.values',{})).length
})

export const useSomeReduxData = () => {
    const { data1, data2 } = useSelector(structuredSelector);

    useEffect(() => {
        // fetch the data here somehow
    },[//somedeps])
    return { data1, data2 };
};

Cheers

Cl茅ment

Most helpful comment

FWIW, I addressed the tradeoffs between hooks and HOCs in my blog post Thoughts on React Hooks, Redux, and Separation of Concerns, and my ReactBoston talk on Hooks, HOCs, and Tradeoffs.

This statement by @TotooriaHyperion isn't quite correct:

But if you put an anonymous function as selector, then the memoization won't work because the selector function has changed.

Memoization is a question of A) if you're using a library like Reselect that does memoization at all, and B) if you are consistently using the same created selector instance each time.

Here's an example:

const selectUsers = state => state.users;
const selectUserId = (state, userId) => userId;

const selectUserNameById = createSelector(
    [selectUsers, selectUserId],
    (users, userId) =>  {
        const user = users[userId];
        return `${user.firstName} ${user.lastName}`;
    }
);

function UserListItem({userId}) => {
    const userName = useSelector(state => selectUserNameById(state, userId));

    // render
}

This should memoize correctly, at least if there was only one instance of UserListItem. If there were multiple instances of UserListItem being shared, then you would need multiple unique instances of selectUserNameById being created.

All 4 comments

what is the impact of hooks on reselect

useSelector don't accept props, so it's always get same param - only the whole state - within one frame, thus it won't break the memoization at all.

But if you put an anonymous function as selector, then the memoization won't work because the selector function has changed.

If you want to reuse selector between multiple instances, you should do

const selector = useCallback(state => {
  // createSelector with deps

},[...deps])
const { data1, data2 } = useSelector(selector);

I prefer to use connect, because mixup the render function with the establishment of data flow and local state/logic might have bad impact on performance.

IMO
1.hooks are better to provide ability of reusing logical unit with local state across components.

2.common logic reuse and global state management is what rxjs/redux'middleware should handle.

3.global state mapping and performance optimize is what connect & reselect should do.

But you can use useSelector in a Container Component where you establish data flow or create handlers for Presentation Component.

To tell the truth, I don't think hooks is proper for any global state management. It's core value is reusing and composing logic unit with local state.

IMO, this maybe the best practice:

export default React.memo(
    connect(....)(ComponentWithLocalStateWhereYouUseHooks)
);

FWIW, I addressed the tradeoffs between hooks and HOCs in my blog post Thoughts on React Hooks, Redux, and Separation of Concerns, and my ReactBoston talk on Hooks, HOCs, and Tradeoffs.

This statement by @TotooriaHyperion isn't quite correct:

But if you put an anonymous function as selector, then the memoization won't work because the selector function has changed.

Memoization is a question of A) if you're using a library like Reselect that does memoization at all, and B) if you are consistently using the same created selector instance each time.

Here's an example:

const selectUsers = state => state.users;
const selectUserId = (state, userId) => userId;

const selectUserNameById = createSelector(
    [selectUsers, selectUserId],
    (users, userId) =>  {
        const user = users[userId];
        return `${user.firstName} ${user.lastName}`;
    }
);

function UserListItem({userId}) => {
    const userName = useSelector(state => selectUserNameById(state, userId));

    // render
}

This should memoize correctly, at least if there was only one instance of UserListItem. If there were multiple instances of UserListItem being shared, then you would need multiple unique instances of selectUserNameById being created.

Hi there, thanks for being thorough and precise, we're nearly 3 months after my first post and we've had time to think about our use of hooks, redux and reselect.

The redux documentation is quite clear on the point you've explained about multiple selectors (basically it's what @markerikson said)

https://react-redux.js.org/next/api/hooks#using-memoizing-selectors

Thanks a lot for your answers !

Was this page helpful?
0 / 5 - 0 ratings

Related issues

maxim1006 picture maxim1006  路  3Comments

sarink picture sarink  路  4Comments

alvaromb picture alvaromb  路  5Comments

watch-janick picture watch-janick  路  5Comments

0x80 picture 0x80  路  5Comments