Search code examples
reduxreselect

react, reselect and redux asyncThunk


how can i use custom groupby function for data that i get from asyncThunk I want to store in "redux store" original array that i obtain from api and then i want to change data(by using groupby function) and display it

for example, I have a function thant call api `

export const getAnimals = createAsyncThunk(
    'animals/getAnimals',
    async function(_, {rejectWithValue}) {
        try {
            const response = await fetch('http://127.0.0.1:3001/animals')
            if (!response.ok) {
                throw new Error('Problem');
            }
            const data = await response.json();
            return data;
        } catch (error) {
            return rejectWithValue(error.message)
        }
    }
);

and such array from api

"animals": [
     {"animal_type": "dog","name": "Jack", "id":1},
     {"animal_type": "cat","name": "Kitty", "id":2},
     {"animal_type": "bird","name": "Red", "id":3},
     {"animal_type": "dog","name": "Tatoshka", "id":4},
     {"animal_type": "dog","name": "Rasl", "id":5},
     {"animal_type": "bird","name": "blue", "id":6},
     {"animal_type": "cat","name": "murr", "id":7},
     {"animal_type": "snake","name": "Van", "id":8},
     {"animal_type": "cat","name": "kshh", "id":9},
     {"animal_type": "dog","name": "Mailo", "id":10},
     {"animal_type": "cat","name": "barsik", "id":11},
     {"animal_type": "monkey","name": "Ika", "id":12}
]

I have a slice with extraReducer

const animalSlice = createSlice({
    name: 'animals',
    initialState: {
        animals: [],
        loading: null,
        error: null,
    },
    extraReducers: {
        [getAnimals.pending]: (state) => {
            state.loading = true;
            state.error = null;
        },
        [ggetAnimals.fulfilled]: (state, action) => {
            state.loading = false;
            state.animals = action.payload;
  
        },
        [getAnimals.rejected]: setError,
    }
})

`

in a companent i do something like that `

    const fitOptions = [];
    {    
        Object.keys(animals).forEach(function(animal_type, index){
            fitOptions.push(
                <Menu.Item key={index}>
                    <Accordion.Title
                        active={activeIndex === index}
                        content={animal_type}
                        index={index}
                        onClick={() => accordionClick(index)}
                    />
                    <Accordion.Content active={activeIndex === index} content={
                        <Form>
                        <Form.Group grouped>
                            {animals[animal_type].map((animal) => 
                                <Form.Checkbox label={animal.name} name='animal_id' value={animal.id} key={animal.id} />
                            )}
                        </Form.Group>
                        </Form>
                    } />
                </Menu.Item>
            );
        })
    }

`

and I have a function groupBu that I earlyer call in reducer and as a result save in store "changed" data, but now I want to store in redux store original array and do a groupBy in reselect `

    const groupBy = (xs, key) => {

        return xs.reduce(function(rv, x) {
            (rv[x[key]] = rv[x[key]] || []).push(x);
            return rv;
            }, {});
        
    }; 

`

But I have an error, because this function starts before i get a result of api call. It's seems it must by Promise object as a result of a call, but I can't find a way to use it in this proupBy function

I will be appreciative for your help

I have tryed to create reselect

`

export const selectAnimaksByFilter = createSelector(
  [selectAllAnimals, selectActiveFilter],
  (allAnimals, activeFilter) => {


   if (!activeFilter) {
           const grouped = groupBy(allAnimals, 'animal_type');
        
            
        return grouped;
    }
    

  }
);

`

and get in a component as const animals = useSelector(selectAnimaksByFilter);


Solution

  • I moves groupBy to component before a render and set with .slice() method

    i.e.

    const animals = useSelector(selectAllAnimals);
    const grouped = groupBy(animals.slice(), 'animal_type');
    

    selectAllAnimals defined in a store

    export const selectAllAnimals = (state) => state.animals.animals;