Redux saga runs in infinite loop even after the PROJECTS_SUCCEDED is triggered again it runs back to the fetchData method. please find the saga code below
import axios from 'axios';
import { put, all, take, call, takeLatest , takeEvery} from 'redux-saga/effects';
import actions from './actions';
const getRequest = () =>{
const data = fetch('https://jsonplaceholder.typicode.com/todos/1')
.then( res => res.json())
.catch(err => {throw err});
return data;
}
function* fetchData(action) {
console.log('fetchdata');
try{
const data = yield call(getRequest);
console.log(data)
yield put({type:actions.PROJECTS_SUCCEDED, payload:data});
}
catch (err){
yield put({type:actions.PROJECTS_FAILED,err:err});
}
}
function* dashboardSaga(){
console.log('saga ran once')
yield takeLatest(actions.projectsRequested, fetchData);
}
export default dashboardSaga;
and the action is invoked in componentDidMount
componentDidMount() {
this.props.projectsRequested();
}
and the root saga is
import { all } from 'redux-saga/effects';
import homeSaga from './dashboard/sagas';
export default function* rootSaga(getState) {
yield all([
homeSaga(),
]);
}
Was able to solve the above issue by changing the lifecycle method to componentWillMount instead of componentDidMount
Here you need use action type for requesting
function* dashboardSaga(){
console.log('saga ran once')
yield takeLatest(actions.PROJECTS_REQUESTING, fetchData);
}
Related
i'm new to Redux and have followed this video https://www.youtube.com/watch?v=93p3LxR9xfM to implement Redux into my MERN template, however it keeps crashing due to "Error: Actions must be plain objects. Use custom middleware for async actions."
I believe the problem might be somewhere in the dispatch function but can't seem to find it.
fetch file:
`export function fetchPosts () {
return function(dispatch) {
fetch('http://localhost:5000/products/5d082bb89501e113334e5c8e')
.then(res => res.json())
.then(posts => dispatch({
type: FETCH_POSTS,
payload: posts
})
);
}
} `
the component:
class MyComponent extends React.Component {
componentWillMount() {
this.props.fetchPosts();
}
render() {
return (
<div></div>
);
}
};
export default connect(null, { fetchPosts })(MyComponent);
You fetchPosts action returns a function, whereas it was expected to return a plain object.
Returning a function is required for async operations in redux. But you need to wire up a middleware called redux-thunk.
https://github.com/reduxjs/redux-thunk
So that, this error will go away.
How to wire up redux-thunk:
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import rootReducer from './reducers'
const store = createStore(
reducers, // your reducers
compose(
applyMiddleware(thunk)
)
)
I'm using a Net Core, React-Redux boiler-plate, and when I run the fetch api action, the reducer state does not change at all.
Here is my action
import axios from "axios";
import config from '../config';
const ROOT_URL = config[process.env.NODE_ENV].api;
export const FETCH_EVENTS = "FETCH_EVENTS";
export function fetchEvents() {
const url = ROOT_URL +"/Event/GetAllEvents";
const request = axios.get(url);
return {
type: FETCH_EVENTS,
payload: request
};
}
my index reducer:
import { combineReducers} from 'redux';
import { routerReducer } from 'react-router-redux';
import dataReducer from './dataReducer'
const reducers = {
events: dataReducer
};
const rootReducer = combineReducers({
...reducers,
routing: routerReducer
});
export default rootReducer;
and my reducer:
import { FETCH_EVENTS } from "../actions/ExtractActions";
export default function (state = [], action) {
switch (action.type) {
case FETCH_EVENTS:
console.log("inside reducer")
return [action.payload, ...state];
}
return state;
}
So I add this code in the Home component:
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchEvents }, dispatch);
}
function mapStateToProps(state) {
return {
events: state.events
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
but when I try to run the action and try to see if the reducer state has changed, I get on console log an empty array for "this.props.events". Even though if I am trying to store api data to the state, I even tried modifying the reducer method and simply returning a string, but this.props.events returns an empty array [] again. I am guessing my redux is not working but I don't know why. I've been debugging all night long
componentWillMount() {
this.props.fetchEvents()
console.log(this.props.events)
}
I found the error. For some reason I had to call this.props.events in the render() method and not componentwillmount.
axios.get() is an async function. That's why you couldn't see the updated state when you logged it right after fetching the events. I would recommend you to use the redux-devtools-extension for debugging. Hope this helps. Cheers!
I have created a database in firebase the schema is below:
Now All I have been trying to do is just have it show up when I do a console log but nothing shows up.
Below is the code for my JobsActions.js
import firebase from 'firebase';
import {
JOBS_FETCH_SUCCESS
} from './types';
export const jobsFetch = () => {
return (dispatch) => {
firebase.database().ref('/jobs')
.on('value', snapshot => {
dispatch({ type: JOBS_FETCH_SUCCESS, payload: snapshot.val() });
});
};
};
This is my reducer:
import {
JOBS_FETCH_SUCCESS
} from '../actions/types';
const INITIAL_STATE = {
// jobs: 'RCCA'
};
export default (state = INITIAL_STATE, action) => {
switch (action.type) {
case JOBS_FETCH_SUCCESS:
console.log(action);
return state;
//return action.payload;
default:
return state;
}
};
This is the JobsList
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { View, Text } from 'react-native';
import { jobsFetch } from '../actions';
class JobsList extends Component {
componentWillMount() {
this.props.jobsFetch();
}
render() {
return (
<View style={{ paddingTop: 20 }}>
<Text>Hello</Text>
</View>
);
}
}
export default connect(null, { jobsFetch })(JobsList);
I have authentication using firebase and its not a connection to firebase thats an issue, From what I see, it seems like maybe the ref path is wrong in the Actions file?
You main problem here is not with Firebase actually, since I believe everything else is allright, but with React-Redux.
When you are connecting a component to the store, the connect function recieves two functions. The first one (usually called mapStateToProps) recieves the state and returns an object that will be added to the props. In this case, you are not using it, so passing null is a valid decision.
The second one (usually called mapDispatchToProps) receives the dispatch as a parameter and should return an object with the functions that will be inserted to the props that can be used to dispatch new actions. In this case, you are just passing an object as the second parameter of the connect { jobsFetch }.
When you do this.props.jobsFetch(); you are actually returning the function that receives the dispatch, so nothing is actually executed.
Your mapDispatchToProps should be something similar to this
const mapDispatchToProps = dispatch => {
return {
jobsFetch : () => dispatch(jobsFetch())
}
}
export default connect(
null,
mapDispatchToProps
)(JobsList)
Here, I'm assuming that you are in fact using Redux thunk since you are returning a function that receives the dispatch as a parameter in your actions.
As you may see, we first call the jobsFetch() in order to get the function that receives the reducer, and then we dispatch it.
Let me know if this does not work! There may be something else that is not correct, but this is something that should be addressed. Hope it helps!
I use fetch-mock, redux-mock-store, promise-middleware to test the redux implementation of my application. I have following code:
import configureMockStore from 'redux-mock-store';
import promiseMiddleware from 'redux-promise-middleware';
import fetchMock from 'fetch-mock';
import thunk from 'redux-thunk';
import createLogger from 'redux-logger';
import { bindActionCreators } from 'redux';
import { ACTION_1, hostNameSearchActions }
from '../../../src/actions/hostNameSearchActions';
const middlewares = [thunk, promiseMiddleware(), createLogger()];
let mockStore = configureMockStore(middlewares);
const SERVICE_URL = 'http://url_to_the_service';
describe('Testing thunk actions', () => {
let store = mockStore({ hostData: { key1 :'value'} });
const aHostNameSearch = bindActionCreators({ ...hostNameSearchActions }, store.dispatch).hostNameSearch;
afterEach(() => {
fetchMock.reset();
fetchMock.restore();
mockStore = configureMockStore(middlewares);
store = mockStore({ hostData: { key1 :'value'} });
});
it('ACTION_1_PENDING, ACTION_1_REJECTED dispatched, payload matches expected payload', (done) => {
fetchMock
.mock(`${SERVICE_URL}`,
404 );
const expectedActions = [
{ type: `${ACTION_1}_PENDING` },
{ type: `${ACTION_1}_REJECTED`, payload: {error: 'test.body.error.message'}}
];
aHostNameSearch().then(() => {
expect(store.getActions()).toEqual(expectedActions);
done();
});
});
});
The problem is that 404 call I am mocking with retchMock always ends up being resolved as ACTION_1_FULFILLED. why would this be the case? Am I mocking the call incorrectly?
Redux Promise Middleware always dispatches a rejected action when given a rejected action. If your mocked action always ends up being a fulfilled action, when you expect a rejected action, it is because the promise payload is fulfilled.
This can happen if you have any side-effects (e.g., any functions that use the then method on the promise) and don't properly pass the error up to the middleware. Without more context, though, it's impossible to give you a definitive answer. It would be helpful if you included your hostNameSearchActions.
I used redux thunkMiddle to implement async action, but it was error when I send Http request in actions, the err is :
VM711:3 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.
at Object.performAction (<anonymous>:3:2312)
at liftAction (<anonymous>:2:27846)
at dispatch (<anonymous>:2:31884)
at Object.dispatch (bundle.js:22661)
at dispatch (<anonymous>:2:1620)
at Object.submitForm (bundle.js:23120)
at Form.submitForm (bundle.js:23168)
at Object.ReactErrorUtils.invokeGuardedCallback (bundle.js:4532)
at executeDispatch (bundle.js:4332)
at Object.executeDispatchesInOrder (bundle.js:4355)
There is my code:
In my action,I use superagent to send request,my code like this:
import superagent from 'superagent'
import async from 'async'
export const onSubmitForm = userInfo => {
async.waterfall([
(done) => {
superagent
.post('/userInfo')
.send(userInfo)
.end((err, res) => {
done(err, res.body)
});
}
], (err, data) => {
return (dispatch) => (dispatch(submitFormAction(data)))
});
};
export const submitFormAction = data => {
return {
type: "USER_INFO",
data
}
};
And This is my entry file,I import thunkMiddle from redux :
import React from 'react';
import {render} from 'react-dom';
import {createStore, applyMiddleware} from "redux";
import { composeWithDevTools } from 'redux-devtools-extension';
import {Provider} from "react-redux";
import reducer from './reducers/index';
import thunkMiddleware from 'redux-thunk';
import {App} from './containers/App';
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunkMiddleware)));
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'));
So,how to solve this problem?
A thunk must return a function - some of your code-paths result in nothing being returned.
try altering your action by wrapping it in a function which you can return:
export const onSubmitForm = userInfo => {
return function(dispatch) {
async.waterfall([
(done) => {
superagent
.post('/userInfo')
.send(userInfo)
.end((err, res) => {
done(err, res.body)
});
}
], (err, data) => {
dispatch(submitFormAction(data))
});
}
};