Reputation: 943
I am using redux-saga in a new create-react-app react project with redux and for some reason it seems redux-saga is not picking up the dispatched action. A console log statement in the reducer shows the action does reach the reducer. The odd thing about all this is that I've based this project off other projects where I have successfully used redux-saga (but are not create-react-app's). I'm wondering if it is a versioning issue or some support package is required that I am missing. Below is a full code snippet.
From my package.json file:
"dependencies": {
"axios": "^0.19.0",
"connect-history-api-fallback": "^1.6.0",
"connected-react-router": "^6.5.2",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"react-google-login": "^5.0.5",
"react-redux": "^7.1.1",
"react-router-dom": "^5.0.1",
"react-scripts": "3.1.1",
"redux": "^4.0.4",
"redux-actions": "^2.6.5",
"redux-immutable-state-invariant": "^2.1.0",
"redux-persist": "^5.10.0",
"redux-saga": "^1.0.5",
"shortid": "^2.2.14",
"styled-components": "^4.3.2",
"styled-modern-normalize": "^0.2.0"
},
"devDependencies": {
"react-hot-loader": "^4.12.11",
"history": "^4.9.0"
},
the outer most index.js file (entry point for my app):
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './components/App';
import store from './store/config';
const render = (Component) => {
ReactDOM.render(
<Provider store={store}>
<Component />
</Provider>,
document.querySelector('#root')
);
};
render(App);
the app.js file is basically a container component with a connected router from connected-react-router which has the app header and then a switch with the app routes. I'm not showing it because it is actually a few nested components and it may just confuse.
I create my store like this:
import {
applyMiddleware,
compose,
createStore,
} from 'redux';
import createSagaMiddleware from 'redux-saga';
import rootReducer from '../root/rootReducer';
import sagas from '../root/rootSaga';
const sagaMiddleware = createSagaMiddleware();
const initialState = undefined;
const store = createStore(
rootReducer(history),
initialState,
applyMiddleware(sagaMiddleware),
);
sagaMiddleware.run(sagas);
export default store;
Next is the root reducer:
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import authReducer from '../auth/authReducer';
import { stateKeys } from '../../types';
export default history => combineReducers({
[stateKeys.ROUTER]: connectRouter(history),
[stateKeys.AUTH]: authReducer,
});
and root saga:
import {
watchRegister,
watchSignin,
watchSignout,
} from '../auth/authSaga';
const root = function* rootSaga() {
yield [
watchRegister(),
watchSignin(),
watchSignout(),
];
};
export default root;
My auth Saga:
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects';
import api from '../../services/api';
import * as actions from '../root/rootActions';
function* signInSaga({ payload }) {
try {
console.log('saga hit...');
// console.log('email: ', email);
// console.log('password: ', password);
// yield put(actions.signinRequested());
// const response = yield call(api.signin, { email, password });
// console.log('response: ', response);
// yield put(actions.signinSucceeded(response));
} catch (error) {
console.log('saga failed...');
yield put(actions.signinFailed());
}
}
export function* watchSignin() {
yield takeEvery(actions.signin.toString(), signInSaga);
}
auth actions:
import { createAction } from 'redux-actions';
export const signin = createAction('auth/signin');
export const signinRequested = createAction('auth/signin_requested');
export const signinSucceeded = createAction('auth_signin_succeeded');
export const signinFailed = createAction('auth_signin_failed');
finally the signin component:
import React, { Component, Fragment} from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { Link, Redirect } from 'react-router-dom';
import * as actions from '../../store/root/rootActions';
import * as selectors from '../../store/root/rootSelectors';
class Signin extends Component {
...
onSubmitClick = (event) => {
event.preventDefault();
this.setState({ loading: true });
const { email, password } = this.state;
const user = {
email,
password
};
// dispatching the action
this.props.signin(user);
};
renderSigninForm() {
const { email, password } = this.state;
return (
<form className="ui large form">
<div className="ui stacked segment">
<div className="field">
<div className="ui left icon input">
<i className="user icon"></i>
<input onChange={this.handleChange("email")} type="email" placeholder="[email protected]" value={email} />
</div>
</div>
<div className="field">
<div className="ui left icon input">
<i className="lock icon"></i>
<input onChange={this.handleChange("password")} type="password" placeholder="Password" value={password} />
</div>
</div>
<div onClick={this.onSubmitClick} className="ui fluid large primary submit button">Log in</div>
</div>
</form>
);
}
render() {
if (this.state.redirectToReferer) {
return <Redirect to="/" />;
}
return (
<div className="ui middle aligned center aligned grid">
<div className="column">
<h2 className="content">Signin</h2>
{this.renderSigninForm()}
</div>
</div>
);
}
}
const mapStateToProps = state => ({
auth: selectors.getAuth(state),
});
const mapDispatchToProps = dispatch => ({
signin: user => dispatch(actions.signin(user)),
});
export default connect(mapStateToProps, mapDispatchToProps)(Signin);
I apologize for the epic length of this post but it's hard to provide a complete code snippet without everything. Any suggestions are welcome :) Thanks in advance
Upvotes: 3
Views: 6742
Reputation: 101
Seems that your root saga file is not calling a saga method to start your sagas. As the documentation says: you should call all()
method to run your saga setup. (e.g https://redux-saga.js.org/docs/introduction/BeginnerTutorial.html).
so your refactored rootSaga.js would look like this:
import { all } from 'redux-saga/effects'
import {
watchRegister,
watchSignin,
watchSignout,
} from '../auth/authSaga';
const root = function* rootSaga() {
yield all([
watchRegister(),
watchSignin(),
watchSignout(),
]);
};
export default root;
Upvotes: 5