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

huangapple go评论80阅读模式
英文:

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

问题

问题是:

我正在尝试在我的React应用程序中使用redux-saga,但仍然出现以下错误:Actions必须是普通对象。使用自定义中间件进行异步操作。代码似乎是正确的,但不知道为什么会出现这个错误。我将非常感谢所有的帮助。我已经与这个问题斗争了大约两天,仍然没有解决方案。我尝试过查找,但仍然出现这个错误。

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;
}
英文:

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 &#39;../../constants&#39;;

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

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

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

reducer...
import { GET_DISTRICTS } from &#39;../../constants&#39;;

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) =&gt; {
	switch (action.type) {
		case GET_DISTRICTS:
			return {
				...state,
				districts: action.payload
			};
		default:
			return state;
	}
};


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

import Select from &#39;react-select&#39;;

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

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

	return (
		&lt;React.Fragment&gt;
			&lt;Select
				name=&#39;adresSelect&#39;
				options={props.address.districts}
				onChange={props.handleDistrictChange}
				placeholder=&#39;Please Select&#39;
			/&gt;
		&lt;/React.Fragment&gt;
	);
};

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

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

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

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

import React from &#39;react&#39;;

import Districts from &#39;./Districts&#39;;

const AddressSearchWidget = (props) =&gt; {
	return (
		&lt;React.Fragment&gt;
			&lt;Districts /&gt;
		&lt;/React.Fragment&gt;
	);
};

export default AddressSearchWidget


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

import * as reducers from &#39;./&#39;;

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;
}


</details>


# 答案1
**得分**: 2

`handleGetDistrictsData` 返回一个 Promise(所有异步函数都返回 Promise)。您不能在普通的 Redux Saga 中分发一个 Promise,Redux Saga 也不改变这一点。相反,分发一个普通的 action,让该 action 运行一个 Saga。然后,该 Saga 可以执行异步操作,当完成时再分发另一个 action。Reducer 只监听第二个 action。

```javascript
// Actions(动作):
export const getDistrictsData = () => ({
  type: GET_DISTRICTS,
})

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

// Sagas(Saga):
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(Reducer):
export default (state = initialState, action) => {
    switch (action.type) {
        case DISTRICTS_DATA_SUCCESS:
            return {
                ...state,
                districts: action.payload
            };
        default:
            return state;
    }
};
```

<details>
<summary>英文:</summary>

`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&#39;s done dispatch another action. The reducer listens only for that second action.

```
// Actions:
export const getDistrictsData = () =&gt; ({
  type: GET_DISTRICTS,
})

export const districtsDataSuccess = (districts) =&gt; ({
  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) =&gt; {
        console.info(item);
        list.push({
            label: item.ADI,
            value: item.ID
        });
    });
    yield put(districtsDataSuccess(list));
}

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

</details>



huangapple
  • 本文由 发表于 2020年1月6日 17:50:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/59609873.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定