I have this simple saga:
export function* priceComparisonSaga() {
yield takeEvery(RECORD_PRICE, priceComparison);
}
But whenever I dispatch the event, it is never called. What's going on?
It turns out the action in question had an extra "type" property.
When using the shortcut to create actions like this,
export const addBalance = makeActionCreator(ADD_BALANCE,"balance","type");
This action won't work, because the second type property overwrites "ADD_BALANCE". Instead, this works:
export const addBalance = makeActionCreator(ADD_BALANCE,"balance","balancetype");
Related
I am getting this error when i am fetching data from api
export function* fetchAllProductsAsync()
{ const response = yield call(loadAllProductsApi)
yield put({type:types.ALL_PRODUCTS_SUCCESS, payload:response.data})
}
export function* productSaga()
{
yield takeEvery({type:types.ALL_PRODUCTS_REQ, fetchAllProductsAsync})
}
export default productSaga
function* rootSaga(){ yield all([productSaga])}
export default rootSaga
You are mixing together takeEvery and put interface.
The put effect expects just one parameter - the action object with type payload etc. However, the interface of takeEvery is different, it expects multiple parameters, where the first one is usually the type of the action as a string and the second one is the saga that gets called.
So your takeEvery call should look like this:
yield takeEvery(types.ALL_PRODUCTS_REQ, fetchAllProductsAsync)
For extra details, check out the docs:
https://redux-saga.js.org/docs/api/#takeeverypattern-saga-args
I am using redux-sage in my application and below is my code
export default function* () {
yield takeLatest(ActionTypes.VALIDATE_INPUT, checkForInputValidity);
yield takeLatest(ActionTypes.ON_REFRESH, onRefresh);
yield takeLatest(ActionTypes.ON_SUBMIT, onSubmit);
}
Is there a way to make sure I call a common generator function before any action is handled. For example,
whenever I dispatch some action, I want to update a variable in redux-state. this is common across all actions. What I am trying to avoid here is to duplicate some piece of common code in every action handler
You can use simple reducer function which skips action type check and track all the actions, and i.e. return the current timestamp:
export default () => + new Date()
You can call take with no arguments or with '*' in order to match all actions, per the docs.
The part that might get tricky (though still totally doable!) is getting the correct order of generator functions. Is it important the universal generator is run before the other generators, or is it ok if they are all run at once asynchronously? The docs on concurrency, fork vs spawn, and running tasks in parallel might illustrate the difference.
Untested, but I think this is what you want:
import {all, takeLatest, takeEvery } from "redux-saga/effects";
function* universal(action) {
// do something
// if dispatching anything here, make sure that the dispatched action doesn't get picked up again and create an infinite loop
}
export default function* () {
// univeral generator is called with a blocking yield
yield takeEvery( '*', universal);
// run individual generators in parallel to each other
yield all ([
takeLatest(ActionTypes.VALIDATE_INPUT, checkForInputValidity),
takeLatest(ActionTypes.ON_REFRESH, onRefresh),
takeLatest(ActionTypes.ON_SUBMIT, onSubmit),
]);
}
Alternatively, you could write some sort of custom middleware.
In the Redux Style Guide, it is strongly recommended to Put as Much Logic as Possible in Reducers:
Wherever possible, try to put as much of the logic for calculating a
new state into the appropriate reducer, rather than in the code that
prepares and dispatches the action (like a click handler).
What I'm not sure of is, if thunks are also considered to be "the code" of some sort. Besides, we've also been (mis?)using thunks to grab data from other slices of state.
Hypothetically simplified code snippet of such thunk:
const addX = x => (dispatch, getState) => {
const { data, view } = getState();
const { y } = view; // <-- here accessing data from `view` state.
const yy = doSomeLogicWith(y);
const z = doSomeMoreLogicWith(yy);
dispatch({ type: 'data/xAdded', payload: { x, z } });
};
Is this actually considered to be an anti-pattern in Redux? If so, what are the cons of doing this?
Yes, a thunk would qualify as "the code that dispatches the action" for this case. So, what the rule is recommending here is that if possible, the action would just contain y, and the function calls to doSomeLogicWith(y) and doSomeMoreLogicWith(yy) would ideally exist within the reducer instead.
Having said that, it's totally fine for a thunk to extract pieces of data from the state and include that in the action, and it's not wrong for a thunk to do some pre-processing of data before dispatching the action.
The style guide rule is just saying that, given a choice between running a particular piece of logic in a reducer or outside a reducer, prefer to do it in the reducer if at all possible.
I have 2 reducers that are combined in a Root Reducer, and used in a store.
First reducer 'AllTracksReducer" is supposed to return an object and the second 'FavoritesReducer' an array.
When I create a container component and a mapStateToProps method in connect, for some reason the returned state of the store is an object with 2 reducer objects which hold data, and not just an object containing correposding data, as expected.
function mapStateToProps(state) {
debugger:
console.dir(state)
//state shows as an object with 2 properties, AllTracksReducer and FavoritesReducer.
return {
data: state.AllTracksReducer.data,
isLoading: state.AllTracksReducer.isLoading
}
}
export default connect(mapStateToProps)(AllTracksContainer);
so, in mapStateToProps, to get to the right state property, i have to say
state.AllTracksReducer.data... But I was expecting the data to be available directly on the state object?
Yep, this is a common semi-mistake. It's because you're using likely using ES6 object literal shorthand syntax to create the object you pass to combineReducers, so the names of the imported variables are also being used to define the state slice names.
This issue is explained in the Redux docs, at Structuring Reducers - Using combineReducers.
Create some selectors that receive the whole state (or the reducer-specific state) and use it in your mapStateToProps function. Indeed the name you define when you combineReducers will be the topmost state keys, so your selectors should take that into account:
const getTracks = (state) => state.allTracks.data
const isLoading = state => state.allTracks.isLoading
This assumes you combine your reducers with allTracks as they key like here:
combineReducers({
allTracks: allTracksReducer
})
And then you can use those selectors in your mapper, like
const mapStateToProps = state => ({
isLoading: isLoading(state),
tracks: getTracks(state)
})
There's a delicate link between your combineReducers call and your selectors. If you change the state key name you'll have to update your selectors accordingly.
It helps me to think of action creators as "setters" and selectors as "getters", with the reducer function being simply the persistence part. You call your setters (dispatching action creators) when you want to modify your state, and use your selectors as shown to get the current state and pass it as props to your components.
Well, that's how it supposed to work. When you're using combineReducers, you're literally mapping the name of a reducer to the reducer function.
If it bothers you, I would suggest a little syntactical magic if you're using es2016 (though it seems you're not) like so:
function mapStateToProps(state) {
const { data, isLoading } = state.allTracksReducer;
return {
data: data,
isLoading: isLoading
}
}
export default connect(mapStateToProps)(AllTracksContainer);
Remember, state is the one source of truth that possesses all your reducers.
React, redux, redux-saga
I dispatch an action, say CREATE_REQUESTED to the store.
Redux-saga runs, and makes the async call to the server.
After the saga completes, i.e. blocks for the next CREATE_REQUESTED I want to execute additional code, from the container/component from which the first CREATE_REQUESTED was initiated.
// pseudo code
class Cmp extends React.component {
onCreateClick(id) {
const record = {id, name: 'alabala'}
// I am missing the .then() part
this.props.dispatch({type: 'CREATE_REQUESTED', record}).then(created => {
console.log(created)
}
}
}
Is there a way to do that? How?
If not, how am I supposed to design this task?
One approach could be to pass (resolve, reject) options along with the action, and make the saga call them on succcess/failure. Seems OK.
https://github.com/yelouafi/redux-saga/issues/161