Search code examples
javascriptreactjsreduxredux-toolkitcolumnsorting

How can we sort entites of createEntityAdapter based on table columns?


I'm using Redux-toolkit with entityAdapter for state mangement in my React App. I want to apply table sorting for each column where my table rows are entities of Redux-toolkit entityAdapter. I want to change the sortComparer fuction like this in my reducer.

sortEntities: (state,action) =>{
return {
...state,
sortComparer:(a:myEntityType,b:myEntityType)=> a.title.localCompare(b.title)
};
},

I dispatch sortEntities action on onClick handler of columns. This sort of changing the sortComparer is not throwing any rules voilation error but not working. Can somebody help me?


Solution

  • What you are doing in the above code is storing a function to the sortComparer property of your state. You aren't actually applying the sort function anywhere, and it's not advisable to store non-serializable data like functions in Redux.

    The adapter that you create by calling createEntityAdapter is an object which helps you interact with the state. The state itself is just an array of ids and a dictionary object of entities. The sortComparer property of the adapter is not part of the state, so it cannot be modified by modifying the state.


    There are a lot of ways to approach this.

    For example, you could select all entities of Redux and sort them locally.

    const useSortedEntities = (sortBy) => {
       // get an array of entities with the selector from the entity adapter
       const allEntities = useSelector(selectEntities);
    
       // sort based on the current property
       // use ... to avoid mutation
       // would probably want to memoize this
       return [...allEntities].sort(
         (a, b) => a[sortBy].localCompare(b[sortBy])
       );
    }
    
    const SomeComponent = () => {
       const [sortProperty, setSortProperty] = useState('author');
    
       const sortedList = useSortedEntities(sortProperty);
    ...
    

    Or you could dispatch an action with the sort property in the payload and save the sort property in Redux. You can then use createSelector to create a selector for the sorted data.

    const mySlice = createSlice({
        name: 'someName',
        initialState: myAdapter.getInitialState({
           // additional properties to include in initial state
           sortBy: 'author'
        }),
        reducers: {
           sortEntities: (state, action: PayloadAction<string>) => {
               state.sortBy = action.payload;
           }
    ...
    
    const selectSortedEntities = createSelector(
       // first select entities with the selector from the adapter
       selectEntities,
       // next select the sort order
       (state: RootState) => state.pathToSlice.sortBy
       // then combine them to return a sorted array
       (allEntities, sortBy) => [...allEntities].sort(
            (a, b) => a[sortBy].localCompare(b[sortBy])
       );
    )
    
    const SomeComponent = () => {
       const sortedList = useSelector(selectSortedEntities);
       
       const dispatch = useDispatch();
       
       const onClick = () => {
           dispatch(sortEntities('title'));
       }
    ...