We are using https://github.com/react-boilerplate/react-boilerplate and have a classic store layout with multiple components.
We have a redux store for adding products to the cart, which has a side effect saga to save the added product to the cart database.
Because there are multiple positions to add products to cart, we have used the same CartContainer on one page 3 times (the Cart itself, the product listing and in another product listing).
The problem we have now, is that the api will be called 3 times.
I guess that is because, by using the container 3 times, we also injected the Saga three times.
My question is now: What is the correct approach to only inject the Saga once, without having to rewrite all of the sagas again and again?
This is my saga:
import {
call,
put,
select,
takeLatest,
} from 'redux-saga/effects';
import {getRequest, putRequest} from '../../utils/request';
import {ADD_PRODUCT, LOAD_PRODUCTS} from './constants';
import {
addProductSuccess,
addProductError,
productsLoaded,
productsLoadingError,
} from './actions';
import {makeSelectProduct, makeSelectUserId} from './selectors';
/**
* Github repos request/response handler
*/
export function* getProducts() {
const requestURL = '/api/orders';
const user_id = yield select(makeSelectUserId());
try {
let itemsData = yield call(getRequest, requestURL, user_id);
if (itemsData) {
itemsData = itemsData.slice(-1).items;
}
yield put(productsLoaded(itemsData));
} catch (err) {
yield put(productsLoadingError(err));
}
}
/**
* Github repos request/response handler
*/
export function* addProduct() {
const requestURL = '/api/cart';
const productData = yield select(makeSelectProduct());
try {
const orderData = yield call(putRequest, requestURL, productData);
yield put(addProductSuccess(orderData.id));
} catch (err) {
yield put(addProductError(err));
}
}
/**
* Root saga manages watcher lifecycle
*/
export default function* root() {
yield [
takeLatest(ADD_PRODUCT, addProduct),
takeLatest(LOAD_PRODUCTS, getProducts),
];
}
And this is the export part of my container:
export function mapDispatchToProps(dispatch) {
return {
onAddProduct: (id, quantity, variant) => {
dispatch(addProduct(id, quantity, variant));
},
onLoadProducts: (user_id) => {
dispatch(loadProducts(user_id));
},
};
}
const mapStateToProps = createStructuredSelector({
products: makeSelectProducts(),
});
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const withReducer = injectReducer({key: 'cart', reducer});
const withSaga = injectSaga({key: 'cart', saga});
export default compose(withReducer, withConnect)(CartContainer);
You have to change the way you inject your saga so it will be injected only once.
In your containers/App/index.js
// ...
import { compose } from 'redux';
import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer';
import reducer from 'containers/CartContainer/reducer';
import saga from 'containers/CartContainer/saga';
function App() { // We don't change this part
// ...
}
// Add this lines
const withReducer = injectReducer({ key: 'cartContainer', reducer });
const withSaga = injectSaga({ key: 'cartContainer', saga });
export default compose(
withReducer,
withSaga,
)(App);
Now in your `containers/CartContainer/index.js``
import React from 'react':
// ...
class CartContainer extends React.Component {
// ...
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
// REMOVE THIS LINES //
// const withReducer = injectReducer({ key: 'cartContainer', reducer });
// const withSaga = injectSaga({ key: 'cartContainer', saga });
export default compose(
// withReducer,
// withSaga,
withConnect,
)(CartContainer);
I guess you are using redux-sagas-injector
which always looked weird to me. The saga has no place inside or around a React component. It looks like an anti-pattern to me. Your component should only dispatch actions and the saga that listens to these actions should simply handle the side effects. I'll suggest to initialize your redux store in a single place and do not use injectReducer
or injectSaga
.
P.S.
Sorry but it looks weird to me seeing saga and reducer next to mapDispatchToProps
and mapStateToProps
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With