I created a selector of users in a group in my immutable redux state:
import { useStore } from 'react-redux';
import { createSelector } from '@reduxjs/toolkit'
const selectGroup = (state) => state.group;
const memoizedUsers = createSelector(
[selectGroup],
(group) => {
return group.get('users').toJS();
}
)
const useMemoizedUsers = () => {
const store = useStore();
return memoizedUsers(store.getState());
}
and then in my component I am displaying a list of the users with the option to delete a user:
const DeleteButton = ({ rowIndex )} => {
const dispatch = useDispatch();
return (
<Button onClick={() => {
dispatch({
type: '@@group/removeUser',
payload: { rowIndex }
})
}}
>
Delete
</Button
)
}
const columnDefs = [
{
headerName: 'Name',
field: 'name',
},
{
headerName: 'Age',
field: 'age',
},
{
headerName: '',
field: 'onDelete',
cellRenderer: ({ node: { rowIndex } )} => {
return (
<DeleteButton rowIndex={rowIndex} />
)
}
}
];
const UserList = () => {
const users = useMemoizedUsers();
return {
<Grid
rowData={users}
columnDefs={columnDefs}
/>
}
}
When I click delete, I see the redux store being updated ==> from 10 users down to 9 users.
However, my UserList
component is not rerendering and the UI still displays all 10 Users.
Anyone know how to force the component to update or am I using the createSelector incorrectly?
If I change const users:
const users = useSelector((state) =>
state.group.get('users')?.toJS()
}
The component rerenders and the user list updates, but I get that redux warning that Selector unknown returned a different result when called with the same parameters. This can lead to unnecessary rerenders.
Further, I can rewrite the UserList
like this:
const UserList = () => {
const users = useSelector((state) =>
state.group.get('users')
}
return {
<Grid
rowData={users.toJS()}
columnDefs={columnDefs}
/>
}
}
Writing the component this way re-renders the UI and the redux warning goes away, but this defeats the purpose of using the createSelector
s.
The useStore
hook doesn't actually subscribe to updates from the store. store.getState
is basically a one-and-done function call, it gets and returns the current state value from the store, once.
const useMemoizedUsers = () => {
const store = useStore();
return memoizedUsers(store.getState()); // <-- doesn't subscribe to store changes
}
createSelector
actually already creates a memoized selector function, so you just use it directly in a useSelector
hook call.
const selectGroup = (state) => state.group;
const selectGroupUsers = createSelector(
[selectGroup],
(group) => group.get('users').toJS()
)
import { useSelector } from 'react-redux';
...
const UserList = () => {
const users = useSelector(selectGroupUsers);
return {
<Grid
rowData={users}
columnDefs={columnDefs}
/>
}
}