Actions must be plain objects. Use custom middleware for async actions Saga thunk I do have so far in my store

115 views Asked by At

The problem is:

I'm trying to use redux-saga in my react app, but i still has this error: Actions must be plain objects. Use custom middleware for async actions. Code it seems correct but no idea why gives that error. I'll be glad for all the help. I'm fighting with it for about two days and still doesn't have a solution. I tried to look up, but I still have this error.

action...
import { GET_DISTRICTS} from '../../constants';

const getAdres = async (url) => {
    let response = await fetch(url);
    let data = await response.json();
    let list = [];
    data.AdresList.Adresler.Adres.forEach((item) => {
        console.info(item);
        list.push({
            label: item.ADI,
            value: item.ID
        });
    });
    return list;
};

export const actions = {
    handleGetDistrictsData: async () => {
        let districts = await getAdres(`url is here`);

        return {
            type: GET_DISTRICTS,
            payload: districts
        };
    },

reducer...
import { GET_DISTRICTS } from '../../constants';

export const initialState = {
    districts: [],
    quarters: [],
    streets: [],
    doors: [],
    districtSelected: false,
    districtSelectedID: null,
    quarterSelected: false,
    quarterSelectedID: null,
    streetSelected: false,
    streetSelectedID: null,
    doorSelected: false,
    doorSelectedID: null
};

export default (state = initialState, action) => {
    switch (action.type) {
        case GET_DISTRICTS:
            return {
                ...state,
                districts: action.payload
            };
        default:
            return state;
    }
};


component...
import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actions as addressActions } from '../../../../redux/actions/address';

import Select from 'react-select';

const Districts = (props) => {
    let [ fetchedData, setFetchedData ] = useState(false);

    useEffect(() => {
        props.handleGetDistrictsData();
        setFetchedData(true);
    });

    return (
        <React.Fragment>
            <Select
                name='adresSelect'
                options={props.address.districts}
                onChange={props.handleDistrictChange}
                placeholder='Please Select'
            />
        </React.Fragment>
    );
};

const mapStateToProps = (state) => ({
    address: state.address
});

const mapDispatchToProps = function(dispatch) {
    return bindActionCreators({ ...addressActions }, dispatch);
};

export default connect(mapStateToProps, mapDispatchToProps)(Districts);

-------------

import React from 'react';

import Districts from './Districts';

const AddressSearchWidget = (props) => {
    return (
        <React.Fragment>
            <Districts />
        </React.Fragment>
    );
};

export default AddressSearchWidget


store...
import { applyMiddleware, combineReducers, compose, createStore } from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootSaga from './sagas/index';

import * as reducers from './';

export function initStore() {
    const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

    const rootReducer = combineReducers(reducers);
    const sagaMiddleware = createSagaMiddleware();

    const store = createStore(rootReducer, composeEnhancer(applyMiddleware(sagaMiddleware)));

    // Run sagas
    sagaMiddleware.run(rootSaga);

    return store;
}

1

There are 1 answers

0
Nicholas Tower On

handleGetDistrictsData returns a promise (all async functions return promises). You cannot dispatch a promise in plain redux saga, and redux-saga does not change this. Instead, dispatch a normal action, and have that action run a saga. The saga can then do async things, and when it's done dispatch another action. The reducer listens only for that second action.

// Actions:
export const getDistrictsData = () => ({
  type: GET_DISTRICTS,
})

export const districtsDataSuccess = (districts) => ({
  type: DISTRICTS_DATA_SUCCESS,
  payload: districts
})

// Sagas:
export function* watchGetDistricts () {
  takeEvery(GET_DISTRICTS, getDistricts);
}

function* getDistricts() {
    let response = yield fetch(url);
    let data = yield response.json();
    let list = [];
    data.AdresList.Adresler.Adres.forEach((item) => {
        console.info(item);
        list.push({
            label: item.ADI,
            value: item.ID
        });
    });
    yield put(districtsDataSuccess(list));
}

// reducer:
export default (state = initialState, action) => {
    switch (action.type) {
        case DISTRICTS_DATA_SUCCESS:
            return {
                ...state,
                districts: action.payload
            };
        default:
            return state;
    }
};