I have 2 actions that have the same payload and only differ in the type. Is there a way to combine the two actions such that I don't have redundant code?
let getData = {type: 'GET_DATA', payload: {id: 1, name: 'sam'}}
let dataSuccess = {type: 'DATA_SUCCESS', payload: {id: 1, name: 'sam'}}
You should ask question to yourself first that why did you create two separate actions if they are doing the same thing. type is only a string to distinguish different actions. If you really need to create two different actions in your case, you can't combine them of course. The only thing you can do probably is to generalize the payload.
Related
I have a case where I get a data returned from API and I need to arrange this data to each reducer. There are two ways of designing it:
Сase when multiple actions are being dispatched to each particular reducer:
export function actionFetchData() {
const data = getDataFromApi()
dispatch({ type: firstReducer.someAction, payload: { data: data.someProp })
dispatch({ type: secondReducer.someAction, payload: { data: data.anotherProp })
// .. and so on
}
Case when only one generic action is being dispatched and only specific reducers should handle this one:
export function actionFetchData() {
const data = getDataFromApi()
dispatch({ type: someGenericAction, payload: { data })
}
I know that the first option is more controllable, for example, what if I need to pass some another data for an action and handle it in a specific reducer.
But the reduxjs docs says: put as much reducer logic as possible in reducers.
That's confusing. What option is more appropriate when designing store?
Per the Redux Style Guide, we do encourage "keeping as much logic as possible in reducers", as well as modeling actions as "events", not "setters".
In this case, there's no need to dispatch two separate actions. Dispatch one action containing all the data from this API response, and let each reducer handle that as appropriate to update its own piece of state.
I'd like to do a collection group query but within a certain path, meaning that I would like to target collections not only with the collectionId but also with where the collection is.
Let's use the example used in the doc to explain it better. We would have landmarks in cities and "general" landmarks in their own collection :
let citiesRef = db.collection('cities');
let landmarks = Promise.all([
citiesRef.doc('SF').collection('landmarks').doc().set({
name: 'Golden Gate Bridge',
type: 'bridge'
}),
citiesRef.doc('SF').collection('landmarks').doc().set({
name: 'Legion of Honor',
type: 'museum'
}),
citiesRef.doc('LA').collection('landmarks').doc().set({
name: 'Griffith Park',
type: 'park'
}),
citiesRef.doc('LA').collection('landmarks').doc().set({
name: 'The Getty',
type: 'museum'
}),
citiesRef.doc('DC').collection('landmarks').doc().set({
name: 'Lincoln Memorial',
type: 'memorial'
})
]);
let generalLandmarks = Promise.all([
db.collection('landmarks').doc().set({
name: 'National Air and Space Museum',
type: 'museum'
}),
db.collection('landmarks').doc().set({
name: 'Ueno Park',
type: 'park'
}),
db.collection('landmarks').doc().set({
name: 'National Museum of Nature and Science',
type: 'museum'
}),
db.collection('landmarks').doc().set({
name: 'Jingshan Park',
type: 'park'
}),
db.collection('landmarks').doc().set({
name: 'Beijing Ancient Observatory',
type: 'museum'
})
]);
Now I would like to query for landmarks that are in a city and not get the general ones. In a simpler way, I would like to do something like this :
let museums = db.collection('cities').collectionGroup('landmarks').where('type', '==', 'museum');
Is it possible ?
This is currently not possible with Cloud Firestore. When you perform a collection group query, it will use all of the collections and subcollections with the given name. You are not able to narrow to scope of the query to specific collections.
What you can do instead is store a field inside the documents in each subcollection that identify which top-level collection they belong to, then use that field to filter your results.
Now I would like to query for landmarks that are in a city and not get the general ones.
No, you cannot. Collection group query will return results from all collections or subcollections with the same name, including the general ones. There is no way you can change this.
The solution for that is to change the name of your collections to be different if you want to limit the scope of your collection group query.
If you app is in productuon and you cannot change the name of your collections, simply try to ignore the results that you get client side. How can you achieve this, just take a look a the path of the document and see from which collections belong. In this way you can only use the result from the collection you need.
Another workaround would be to add a new property of type boolean in your collections named isGeneral and set it tu true in the general collection and false in the others. To get only the particular items and not the general once, add to your query a where call:
whereEqualTo("isGeneral", false);
I followed flow docs and typed redux action creators using union (https://flow.org/en/docs/react/redux/#toc-typing-redux-actions)
so I have a file with ALL the actions gathered into 1 union like in example:
type Action =
| { type: "FOO", foo: number }
| { type: "BAR", bar: boolean }
| { type: "BAZ", baz: string };
Action type is imported in my reducers and used as in exxample from docs:
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FOO": return { ...state, value: action.foo };
case "BAR": return { ...state, value: action.bar };
default:
(action: empty);
return state;
}
}
The problem:
As I mentioned I gathered ALL the actions in one file - currently ~600 actions in one union. I noticed that lately flow server takes crazy time to start (100+ seconds), rechecking flow is also a pain if change is related to reducer. According to flow logs, files that contain reducers are marked as "Slow MERGE" - 15 to 45s.
After experimenting, I noticed that changing my Action type to any cuts the time from 100s to 9s.
The question:
can this be related to huge Action union?
should I split it into a few smaller types which will contain only actions to import in particular reducer or this is a wrong way to fix my issue?
It's probably more likely that this one action type is used across your entire app. Any time you make a change to it, Flow needs to recheck a very large number of files. One way to help mitigate this is to ensure all your union actions are in files of their own that don't import other files. Flow can get slow if it has "cycles". One type imports another time which then imports the first time. This can happen if, for example, you define your reducer actions in the reducers themselves. This causes a cycle. Instead, move your action types to their own file.
Additionally, you can use flow cycle to output a dot file you can then visualize this file in something like Gephi https://gephi.org/ to detect cycles.
I have a Redux application that shows a list of posts. The state is more or less this:
{
posts: [
{id: 1, title: 'My Post'},
{id: 2, title: 'Also this one'},
{id: 3, title: 'Other Post'}
],
visible_post_ids: [1, 2]
}
Whenever I load some posts I add them to posts, then I replace the content of visible_post_ids.
This is my action creator for loading posts:
function loadPosts (filters) {
return function (dispatch, getState) {
return fetch(`/posts.json?filters=${filters}`)
.then((response) => response.json())
.then((posts) => {
dispatch(postsLoaded(posts)) // Will update `posts`
const postIds = posts.map((post) => post.id)
dispatch(updateVisiblePosts(postIds)) // Will update `visible_post_ids`
})
}
}
My question is: is it idiomatic to dispatch two (or more) events from a thunk? Or should I dispatch only one and handle it in various reducers?
Quick answer : there is no problem to dispatch two or more actions from a thunk, I think it's a good practice,especially if API Call response contains answers to two completely different concerns.
I think it depends what you are trying to represent, in your case you can have one action that represent an add of new posts and two different reducers can catch it and do different tasks with it.
But you can see that as two different actions (your example) and it's great too.
As Sergey L said, in your case with a unique action (for your case) it can create an interesting "dependency"
If you don't consider scenario when it is possible to postsLoaded without calling updateVisiblePosts, it is better to handle the state change just in postsLoaded.
Especially if you need them to be in sync. For example, if you need a grantee that visible_post_ids does not contains Ids from not existing/loaded posts. Besides it minimizes the updates as each dispatch will cause processing in React.
On the other hand, having these actions separate can make code more clear as you have very simple implementation for each action.
I set up the aggregation rule:
{{ object.experienceId }}
on a notification feed in getstream.io expecting it to aggregate based on the object.experienceId, but instead it seems to aggregate everything into one, regardless of object.experienceId. Am I mis-understanding how aggregation works? What could be the issue?
var activity = {
time: new Date(),
verb: 'created',
actor: { id: 1, name: 'User One' },
object: {
id: 2,
experienceId: 12,
caption: 'Moment 1',
photo:
{ id: '314e00a2-2455-11e5-b696-feff819cdc9f',
mainColor: 'ff3333',
width: 1000,
height: 400 },
createdBy: {
id: 1, name: 'User One'
},
type: 'Moment' },
context: 'http://derbyapp.co'
};
notifications.addActivity(activity,
The reason why this is not working is because the object field is expected to be a string (http://getstream.io/docs/#add-remove-activities), thus within the aggregation rule you can not reference properties of activities object field. There are multiple solutions to this problem.
First you could supply the experienceId as a separate property of the activity object, so you can use the aggregation template {{ experienceId }}, since all the additional properties provided to an activity can be used in the aggregation rule (http://getstream.io/docs/#aggregated-feeds).
Second you could supply an object on any additional field of the activity, for instance item. Additional fields can reference their child properties thus you could use aggregation rule {{ item.experienceId }}. But beware not to send data to the getstream.io API that is not actually needed at getstream.io's end, in this example you could also send the object's id field, instead of the entire object, and retrieve the object from your local database once you retrieve activities from the API (The same holds for the actor field). If you do not want to take care of the logic needed for this you could use one of getstream's integration libraries (there are libraries for rails/django/laravel etc.).
var activity = {
time: new Date(),
verb: 'created',
actor: 1,
object: '1',
experienceId: 12
};