Why is my redux-observable epic not being triggered? - javascript

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:

Related

is there a way to call another action from one action in react redux

I have two action in my redux actions... now I have a login form that makes a post request.. now I want to call another action from within the login action.
Now i connected the login function but not the setLoading function
e.g
export const loginUser = ()=>dispatch=>{
setLoading();
}
export const setLoading = ()=>({
type: SET_LOADING
})
You can use the logic of this answer to help you. An example is given in this answer(That's an example of getting products).
React Redux fetching data from backend approach
For your problem, it's best to pay attention to this part of the link
I gave you above, since you can use that logic for your program.
Notice this in the link above
// redux/product/product.actions.js
import { ShopActionTypes } from "./product.types";
import axios from "axios";
export const fetchProductsStart = () => ({
type: ShopActionTypes.FETCH_PRODUCTS_START
});
export const fetchProductsSuccess = products => ({
type: ShopActionTypes.FETCH_PRODUCTS_SUCCESS,
payload: products
});
export const fetchProductsFailure = error => ({
type: ShopActionTypes.FETCH_PRODUCTS_FAILURE,
payload: error
});
export const fetchProductsStartAsync = () => {
return dispatch => {
dispatch(fetchProductsStart());
axios
.get(url)
.then(response => dispatch(fetchProductsSuccess(response.data.data)))
.catch(error => dispatch(fetchProductsFailure(error)));
};
};

typesafe-actions(createStandardAction) not working on server with redux

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

React/Redux how to access the state in the networkservice

I have created a Network service component which deals with the API call. I want to retrieve state from other components which update the store.
Im having trouble getting the state so I started using Redux, but I havent used Redux before and still trying to find a way to pass the state to the NetworkService. Any help would be great, thanks!
Here is my NetworkService.js
import RequestService from './RequestService';
import store from '../store';
const BASE_URL = 'api.example.com/';
const REGION_ID = //Trying to find a way to get the state here
// My attempt to get the state, but this unsubscribes and
// doesnt return the value as it is async
let Updated = store.subscribe(() => {
let REGION_ID = store.getState().regionId;
})
class NetworkService {
getForecast48Regional(){
let url =`${BASE_URL}/${REGION_ID }`;
return RequestService.getRequest(url)
}
}
export default new NetworkService();
store.js
import {createStore} from 'redux';
const initialState = {
regionId: 0
};
const reducer = (state = initialState, action) => {
if(action.type === "REGIONAL_ID") {
return {
regionId: action.regionId
};
}
return state;
}
const store = createStore(reducer);
export default store;
My folder heirarchy looks like this:
-App
----Components
----NetworkService
----Store
Do not import store directly. Use thunks/sagas/whatever for these reasons.
NetworkService should not know about anything below.
Thunks know only about NetworkService and plain redux actions.
Components know only about thunks and store (not store itself, but Redux's selectors, mapStateToProps, mapDispatchToProps).
Store knows about plain redux actions only.
Knows - e.g. import's.
//////////// NetworkService.js
const networkCall = (...args) => fetch(...) // say, returns promise
//////////// thunks/core/whatever.js
import { networkCall } from 'NetworkService'
const thunk = (...args) => (dispatch, getState) => {
dispatch(startFetch(...args))
const componentData = args
// I'd suggest using selectors here to pick only required data from store's state
// instead of passing WHOLE state to network layer, since it's a leaking abstraction
const storeData = getState()
networkCall(componentData, storeData)
.then(resp => dispatch(fetchOk(resp)))
.catch(err => dispatch(fetchFail(err)))
}
//////////// Component.js
import { thunk } from 'thunks/core/whatever'
const mapDispatchToProps = {
doSomeFetch: thunk,
}
const Component = ({ doSomeFetch }) =>
<button onClick={doSomeFetch}>Do some fetch</button>
// store.subscribe via `connect` from `react-redux`
const ConnectedComponent = connect(..., mapDispatchToProps)(Component)

redux-react error: Actions must be plain objects

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.

Redux-Thunk - Async action creators Promise and chaining not working

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,
),
);

Categories