i`m learning redux and after setting all the setup i get this message:
Error: Actions must be plain objects. Use custom middleware for async actions.
I readed 10 different issues with this mistake but not single solution works for me. here is my project on github, in case problem not in following code:https://github.com/CodeNinja1395/Test-task-for-inCode/tree/redux
store:
import {createStore, applyMiddleware, compose} from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
const initialState = {};
const middleware = [thunk];
const store = createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
export default store;
actions:
import {FETCH_DATA} from './types';
export const fetchData = () => dispatch => {
fetch('https://raw.githubusercontent.com/CodeNinja1395/Test-task-for-inCode/master/clients.json')
.then(posts =>
dispatch({
type: FETCH_DATA,
payload: posts
})
);
};
You use an old version of redux - v1, while the new version is v4. When you upgrade to v4 it works.
npm install redux#^4.0.0
You're not returning your fetch() call. You have to make sure you return your fetch call inside of the action creator.
export function fetchData() {
return function(dispatch) {
return fetch( ... ).then( ... ).catch( ... )
}
}
export const fetchData = (params) => dispatch => {
dispatch(beginAjaxCall());
return fetch(...).then(response => {
dispatch(someFunction(response));
}).catch(error => {
throw(error);
});
}
export const loadIncidents = (params) => dispatch => {
dispatch(beginAjaxCall());
return IncidentsApi.getIncidents(params).then(incidents => {
dispatch(loadIncidentsSuccess(incidents));
}).catch(error => {
throw(error);
});
}
The top is ES5, middle ES6, and third is an ES6 version of a function that I used in a project.
Hope that helps.
Related
I can't update my store values using action creator. All needed values I get from component normally, but just can't substitute the initial state values with them. I believe I made a stupid mistake or even misspelled something, but I spent a lot of time already, and the console in the browser still shows me the empty strings in that reducer's state.
let initialstate = {
login: "",
password: ""
}
const formReducer = (state = initialstate ,action) => {
switch (action.type) {
case 'SET-FORM-DATA': {
return {
...state,
login: action.login,
password: action.password
}
}
default: return state;
}
}
export const SetFormData = (login, password) => ({
type: 'SET-FORM-DATA', login, password
})
export default formReducer;
That formReducer is normally assigned in redux-store
import { applyMiddleware, combineReducers, createStore } from 'redux';
import authReducer from './auth-reducer';
import dialogsReducer from './dialogs-reducer';
import profileReducer from './profile-reducer';
import usersReducer from './users-reducer';
import thunkMiddleware from 'redux-thunk';
**import formReducer from './form-reducer';**
let reducers = combineReducers({
profilePage: profileReducer,
dialogPage: dialogsReducer,
usersPage: usersReducer,
auth: authReducer,
form: formReducer
})
let store = createStore(reducers, applyMiddleware(thunkMiddleware));
window.store = store;
export default store;
I call this action creator on submit of form like that:
const onSubmit = formData => {
props.SetFormData(formData.Login, formData.password)
}
And this call seems to be, luckily works alright
I am Brazilian and therefore I speak Portuguese, but I will use the translator to try to help you.
I didn't quite understand the problem, but to handle redux we must use dispatch. You are not using this, you are just passing redux values. Sorry if I got it wrong, but I use something like this:
import { createTypes } from "reduxsauce";
export const gradesTypes = createTypes(
`
SET_GRADES
RESET_STATE
`,
{ prefix: "GRADES/" }
);
const setGrades = (grades) => {
return (dispatch) => dispatch({ type: gradesTypes.SET_GRADES, grades });
};
const resetState = () => {
return (dispatch) => dispatch({ type: gradesTypes.RESET_STATE });
};
export const gradesActions = {
setGrades,
resetState,
};
trying to understand rxjs and rxjs within redux and redux observables by trying to do a simple fetch example
got my store set up like so:
import { applyMiddleware, createStore } from 'redux'
import { reducers } from 'redux/reducers'
import { createEpicMiddleware } from 'redux-observable'
import rootEpic from '../epics'
const epicMiddleware = createEpicMiddleware()
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
epicMiddleware.run(rootEpic)
export const store = createStore(reducers, composeEnhancers(applyMiddleware(epicMiddleware)))
and in my epic I've got
const getUserDataEpic = (action$, state$) =>
action$.pipe(
ofType('GET_USER_DATA'),
mergeMap(async (action) => {
const url = `my-url-is-here`
const data = await fetch(url).then((res) => res.json())
return Object.assign({}, action, { type: 'GET_DATA_SUCCESS', data })
}),
catchError((err) => Promise.resolve({ type: 'FETCH_ERROR', message: err.message })),
)
const epic2 = (action$, state$) => {}
export default combineEpics(getUserDataEpic)
I also have my action creator:
export const fetchData = () => ({ type: 'GET_USER_DATA' })
this gets fired in my component on mount. I've wrapped in mapDispatchToProps and I've verified it's definitely getting called. as is my reducer
I don't understand why my epic is not being triggered tho?? I was hoping it would see the GET_USER_DATA being fired and then fire it's own action to put the resolved API request into my state.
please advise where im going wrong
ok I figured it out
export const store = createStore(reducers, composeEnhancers(applyMiddleware(epicMiddleware)))
epicMiddleware.run(rootEpic)
I had to call these the other way around ^ :facepalm:
I'm trying to dispatch an action which is by using createStandardAction(typesafe-actions) and then it goes to epic(redux-observable) for api call.
The strange part is that it works perfectly with stub data, it completes the flow(i.e., component->action->epic->reducer->store) but the action doesn't trigger or enters the epic while using it with the actual server
**Component:-**
export const mapDispatchToProps = (dispatch: Dispatch): ReduxActions => ({
loadTestData: () => dispatch(loadTestData())
})
export default connect(
mapStateToProps,
mapDispatchToProps
)(withNavigation(loadData))
**Action**
import { ActionsUnion, createStandardAction } from 'typesafe-actions'
export const LOADDATA_GET = 'LOADDATA_GET'
export const loadData = createStandardAction(LOADDATA_GET)<void>()
const actions = {
loadData
}
export type AllActions = ActionsUnion<typeof actions>
**Epic**
import { Action, MiddlewareAPI } from 'redux'
import { ActionsObservable, Epic } from 'redux-observable'
import { Observable } from 'rxjs'
import {
LOADDATA_GET
} from './loadData.actions'
export const getloadDataEpic: Epic<Action, ReduxState> = (
action$: ActionsObservable<any>,
store: MiddlewareAPI<any, ReduxState>,
{ mobileAPI }: EpicDependencies
) =>
action$
.ofType(LOADDATA_GET)
.mergeMap((action) => {
return Observable.merge(
mobileAPI
.getJSON('/dummypath/loadData')
.mergeMap((response) => {
return Observable.of<any>(
setLoadData(response)
)
})
)}
)
.catch((error) => {
return Observable.of(errorAction(error))
})
I am really confused why the flow doesn't comes to epic for the actual server while for local json data and dummy path it works
Issue fixed, there was some data-mapping issue on the server side
SomeAction is not a function. ( In 'SomeAction()', 'SomeAction' is undefined ).
I get this error when i execute the SomeAction function.
If i only have SomeAction in my actions file and i do
export default SomeAction;
and then import it as below
import SomeAction from 'path/to/action'
it works fine. But since i want more than one functions, i did the following.
this is my Actions.js
const SomeAction = () => dipatch => ({
// Code here
});
const AnotherAction = () => dispatch => ({
// Code here
});
export default { SomeAction, AnotherAction };
then in my App.js
import { SomeAction } from 'path/to/action';
// Here the eslint gives me an error -> 'SomeAction not found in "path/to/action"'
const App = ({ SomeAction }) => {
// Code here
};
App.propTypes = {
SomeAction: PropTypes.func,
}
const mapStateToProps = state => ({
error: state.user.error,
});
export default connect(
mapStateToProps,
{ SomeAction }
)(App);
This worked on a React web app i was coding. Why not in React-Native?
Reviewing, I see you are exporting by default two methods. Normal implementation is one method. Another solution to do this is exporting one by one methods and importing them with their names.
Example of Exporting:
export const SomeAction = () => dipatch => ({
// Code here
});
export const AnotherAction = () => dispatch => ({
// Code here
});
Example of Importing:
import { SomeAction, AnotherAction } from 'path/to/action';
This example is a normal way to export and import functions.
You can not have two default methods exported.
export const SomeAction = () => dipatch => ({
// Code here
};
export const AnotherAction = () => dispatch => ({
// Code here
};
they will be available in your App component in as follow:
import { SomeAction , AnotherAction} from 'path/to/action';
Importing a Defautl export is as follow
import { SomeAction } from 'path/to/action'; or import SomeAction from 'path/to/action';
just use this abpve export const way
I am trying to dispatch an action. I found working examples for some actions, but not as complex as mine.
Would you give me a hint? What am I doing wrong?
I am using TypeScript and have recently removed all typings and simplified my code as much as possible.
I am using redux-thunk and redux-promise, like this:
import { save } from 'redux-localstorage-simple';
import thunkMiddleware from 'redux-thunk';
import promiseMiddleware from 'redux-promise';
const middlewares = [
save(),
thunkMiddleware,
promiseMiddleware,
];
const store = createStore(
rootReducer(appReducer),
initialState,
compose(
applyMiddleware(...middlewares),
window['__REDUX_DEVTOOLS_EXTENSION__'] ? window['__REDUX_DEVTOOLS_EXTENSION__']() : f => f,
),
);
Component - Foo Component:
import actionFoo from 'js/actions/actionFoo';
import React, { Component } from 'react';
import { connect } from 'react-redux';
class Foo {
constructor(props) {
super(props);
this._handleSubmit = this._handleSubmit.bind(this);
}
_handleSubmit(e) {
e.preventDefault();
this.props.doActionFoo().then(() => {
// this.props.doActionFoo returns undefined
});
}
render() {
return <div onClick={this._handleSubmit}/>;
}
}
const mapStateToProps = ({}) => ({});
const mapDispatchToProps = {
doActionFoo: actionFoo,
};
export { Foo as PureComponent };
export default connect(mapStateToProps, mapDispatchToProps)(Foo);
Action - actionFoo:
export default () => authCall({
types: ['REQUEST', 'SUCCESS', 'FAILURE'],
endpoint: `/route/foo/bar`,
method: 'POST',
shouldFetch: state => true,
body: {},
});
Action - AuthCall:
// extremly simplified
export default (options) => (dispatch, getState) => dispatch(apiCall(options));
Action - ApiCall:
export default (options) => (dispatch, getState) => {
const { endpoint, shouldFetch, types } = options;
if (shouldFetch && !shouldFetch(getState())) return Promise.resolve();
let response;
let payload;
dispatch({
type: types[0],
});
return fetch(endpoint, options)
.then((res) => {
response = res;
return res.json();
})
.then((json) => {
payload = json;
if (response.ok) {
return dispatch({
response,
type: types[1],
});
}
return dispatch({
response,
type: types[2],
});
})
.catch(err => dispatch({
response,
type: types[2],
}));
};
From redux-thunk
Redux Thunk middleware allows you to write action creators that return
a function instead of an action
So it means that it doesn't handle your promises. You have to add redux-promise for promise supporting
The default export is a middleware function. If it receives a promise,
it will dispatch the resolved value of the promise. It will not
dispatch anything if the promise rejects.
The differences between redux-thunk vs redux-promise you can read here
Okay, after several hours, I found a solution. redux-thunk had to go first before any other middleware. Because middleware is called from right to left, redux-thunk return is last in chain and therefore returns the Promise.
import thunkMiddleware from 'redux-thunk';
const middlewares = [
thunkMiddleware,
// ANY OTHER MIDDLEWARE,
];
const store = createStore(
rootReducer(appReducer),
initialState,
compose(
applyMiddleware(...middlewares),
window['__REDUX_DEVTOOLS_EXTENSION__'] ? window['__REDUX_DEVTOOLS_EXTENSION__']() : f => f,
),
);