I have the following BuiltMap get map; in the BowlerState. It contains BowlerEntity objects.
I want to update a particular bowler in this map. My BowlerUpdate action contains the values that need to be updated in that particular bowler.
How do I do so? I need to modify the below reducer to do so.
One reducer is like this
BowlerState _updateBowlerAge(
BowlerState bowlerState, SaveBowlerAgeSuccess action) {
return bowlerState.rebuild((b) => b
..map[action.bowler.id] = action.bowler
);
}
Use replace() to replace the desired BowlerEntityBuilder.
BowlerState _updateBowlerAge(BowlerState state, SaveBowlerAgeSuccess action) {
final newMap = state.map[action.bowler.id].toBuilder()
..replace(action.bowler);
return state.rebuild((b) => b
..map.replace(newMap));
}
Related
What is a better way to create reducers with handleActions in redux-actions:
1. Create reducers for each CRUD operations (like add data, delete data) and combine it. How set initialState in this case?
2. Set actions in one reducer (fetchDeleteDataRequest, fetchDeleteDataSuccess, fetchAddDataRequest, fetchAddDataSuccess by example)?
You can have sperate reducers and or common reducers to add or delete data that is up to you. If you are considering separate actions for add and delete you will need to keep the data consistent. But having a common function to deal with the CRUD operations is ideal since this will reduce the amount of code that you have to use but you will need a way to distinguish them as well (maybe bypassing some variable ADD or DELETE). let's consider and list of items that you will be adding or deleting. This list can be an empty array in the beginning (initialState) and pass it as props to your component.
Actions
export const addDeleteItem = data => dispatch => {
// you can perform REST calls here
dispatch({
type: ADD_REMOVE_DATA,
payload: data
});
};
Reducers
let initialState = {
items:[]
}
export default (state = initialState, action) => {
switch (action.type) {
case ADD_REMOVE_DATA:
if(action.payload.event === 'ADD'){
return {...state,items:[...state.items,action.payload.item]}
}else if(action.payload.event === 'DELETE'){
return {...state,items:state.items.filter(item=>item.id !== action.payload.item.id)}
}else if (action.payload.event === 'UPDATE'){
//handle your update code
}
}
}
This is just an example you can follow something like this to avoid code duplication.
i am new in Vue JS and in Firebase. My target is get all 'eventos' that has same category. I mean, let's i have two eventos, an eventos category "SMIX" and another has "DAM". Now i want to get the eventos has category 'SMIX'
My data structure is here :
created() {
var datos = []
firebase.database().ref('usuarios').on("value", data => {
data.forEach(function(user){
user.child("eventos").orderByChild("categoria").equalTo("SMIX")
.forEach(function(evento){
datos.push(evento.val())
});
});
this.eventos = datos;
});
}[My data Structure][1]
There are several errors and points to be noted in your code:
Firstly, if you receive the error user.child(...).orderByChild is not a function
it is because with data.forEach(function(user) {...}), user is a DataSnapshot (see the forEach() doc), and by calling the child() method on this DataSnapshot you get another DataSnapshot... which does not have a orderByChild() method.
The orderByChild() method is a method of a Reference, so you need to do
user.child(...).ref.orderByChild()
using the ref property of the DataSnapshot
Secondly, you cannot do
user.ref.child("eventos").orderByChild("categoria").equalTo("SMIX")
.forEach()
because you need to use the once() or on() methods to get the data at a database location represented by a Reference.
Thirdly, Since you are going to execute several queries within a loop, you need to use the once() method instead of the on() method. The on() method set a listener that continuously "listens for data changes at a particular location."
Finally, note that you need to use Promise.all() to manage the parallel asynchronous queries to the database.
So, having noted all the points above, the following code will do the trick (to put in created()):
var datos = []
firebase.database().ref('usuarios').once('value')
.then(dataSnapshot => {
var promises = [];
dataSnapshot.forEach(user => {
promises.push(user.ref.child("eventos").orderByChild("categoria").equalTo("SMIX").once('value'));
});
return Promise.all(promises);
})
.then(results => {
//console.log(results);
results.forEach(dataSnapshot => {
dataSnapshot.forEach(evento => {
datos.push(evento.val());
});
});
this.eventos = datos;
});
My use case comprises of dispatching two actions from a recursive function (if else construct ) the if part which adds a row in an array ( which is a state of my app) and the else part adds another row and needs to access the length of the array before and call the function itself. What I see here is the length of the array remains same after the first action is being dispatched and thus the call to itself doesn't get the actual value of the length .
My assumption is you are trying to do the second check/call after the first within the component. The component must wait for the new props on the next render. You should move your logic into your action. This is just a guess without more details to the question. Here is an example:
const myAction = (stuff) => {
return (dispatch, getState) => {
let oldLength = getState().myState.stuff.length
dispatch(doStuffToStuff(stuff))
let newLength = getState().myState.stuff.length
dispatch(moreStuffToLength(newLength))
}
}
Thanks the issue was resolved .After every dispatch if the state changes it is required to access the new state by ysing getState() I wasn't doing that .
For example we have reducer photos, which handles array of photos via actions ADD_PHOTO and REMOVE_PHOTO. And if we have arrays users and posts, they both have field for array of photos.
So, in order to avoid code duplicates I'm going to do the following:
Create reducer user = combineReducers(..., photos, ...)
Create actionCreator updateUser
const updateUser = (id, subAction) => ({
type: UPDATE_USER,
payload: {
id,
subAction
}
})
Create reducer users (Here I'm using Immutable.js)
function users(state = List(), action) {
switch (action.type) {
//...
case UPDATE_USER:
const { id, subAction } = action.payload
const index = state.findIndex(user => user.id == id)
return state.updateIn(
[index, 'photos'],
state => photos(state, subAction)
)
break
//...
default:
return state
}
}
And then I'm going to use all of it like this:
dispatch(updateUser(id, addPhoto(url)))
Is this a correct solution of my problem?
Why not simply dispatch both in the place where this is initiated by the user?
dispatch(updateUser(id));
dispatch(addPhoto(url));
I haven't come across this pattern you're applying before. It seems a bit unusual that one reducer is responsible for reducing state of another. Creates a dependency between them that doesn't feel very pure. I'm not even sure one reducer should be able to/can see the state of another.
So no idea about "correct", but I'd say it's not ideal to do it your way. I'd try dispatching both sequentially or maybe in a sort of meta-action that takes care of nested updates and dispatches actions to multiple reducers.
I am converting Mongo.Cursor to array using fetch() in Tracker.autorun and assigning it to the songsArray. But each time the underlying database is changed(reactively), I see duplicate values in songsArray
private songsArray:Array<any>;
songsCursor:Mongo.Cursor<any>;
//...Some code here
ngOnInit():any {
//... Some code here
this.songsCursor = Songs.find();
Tracker.autorun(() => {
this.songsArray = [];
this.songsArray = this.songsCursor.fetch();
});
}
Why is it happening and if I assume I am doing it wrong, then what is the correct way to convert cursors to array in Tracker.autorun?
In your constructor you need to do something like this:
$reactive(this).attach($scope);
// Subscribe to collections here:
this.subscribe('songs');
this.helpers({
songs: () => Songs.find()
});
and you'll find songs is an array like you want