I have an async api call where I get an array of objects and then I map that to dynamically registered modules in my store. Something like this:
dispatch
// before this dispatch some api call happens and inside the promise
// iterate over the array of data and dispatch this action
dispatch(`list/${doctor.id}/availabilities/load`, doctor.availabilities);
The list/${doctor.id} is the dynamic module
action in availabilities module
load({ commit }, availabilities) {
const payload = {
id: availabilities.id,
firstAvailable: availabilities.firstAvailable,
timeslots: [],
};
// then a bunch of code that maps the availabilities to a specific format changing the value of payload.timeslots
commit('SET_AVAILABILITIES', payload)
}
mutation
[types.SET_TIMESLOTS](state, payload) {
console.log(payload);
state.firstAvailable = payload.firstAvailable;
state.id = payload.id;
state.timeslots = payload.timeslots;
}
When I check my logs for the console.log above each doctor has different arrays of time slots Exactly the data I want. However, in the vue developer tools and what is being rendered is just the last doctor's timeslots for all of the doctors. All of my business logic is happening in the load action and the payload in the mutation is the correct data post business logic. Anyone have any ideas why I'm seeing the last doctor's availabilities for every doctor?
It looks like you are assigning the same array (timeslots) to all doctors.
When you add an element to the array for one doctor, you mutate the array that all doctors are sharing.
However with the little code you show, it's difficult to know where is the exact problem.
Related
I'm currently building a Vue app that consumes data from the Contentful API. For each entry, I have a thumbnail (image) field from which I'd like to extract the prominent colours as hex values and store them in the state to be used elsewhere in the app.
Using a Vuex action (getAllProjects) to query the API, run Vibrant (node-vibrant) and commit the response to the state.
async getAllProjects({ commit }) {
let {
fields: { order: order }
} = await api.getEntry("entry");
let projects = order;
projects.forEach(p =>
Vibrant.from(`https:${p.fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (p.fields.accent = palette.Vibrant.hex))
);
console.log(projects);
// Commit to state
commit("setAllProjects", projects);
}
When I log the contents of projects right before I call commmit, I can see the hex values I'm after are added under the accent key. However, when I inspect the mutation payload in devtools, the accent key is missing, and so doesn't end up in the state.
How do I structure these tasks so that commit only fires after the API call and Vibrant have run in sequence?
You cannot add a property to an object in Vue and have it be reactive; you must use the Vue.set method.
Please try replacing that forEach block with the following, which adds the new property using Vue.set:
for (i=0; i<projects.length; i++)
Vibrant.from(`https:${projects[i].fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (Vue.set(projects[i].fields, accent, palette.Vibrant.hex)))
);
UPDATE: changing the format from forEach to a conventional for loop may be gratuitous in this case, since the assignment being made is to an object property of projects and not to a primitive.
I'm not spending a lot of time on StackOverflow, and if the above answer works, I am happy for you indeed.
But I expect from that answer you will get console warnings telling you not to mutate state directly.
Now when this happens, it's because while Vue.set(), does in fact help Vue understand reactively a change has been made, potentially deeply nested in an object.
The problem here is that since you are looping the object, changing it all the time, the commit (Mutator call) is not the one changing state - Vue.set() is actually changing it for every iteration.
I have two async requests I am trying to fulfill, the second based upon the results of the first. The way I am trying to do this is by:
Listen for success of first action: actions.GetAllItems
Select out from the store the relevant items based on ID: this.store.select(selectors.getItemsById)
Map over the returned IDs so I can make the second call for each item in the array of IDs returned by the first call
Put results in redux store, render to view.
The way I have now does successfully put it in my redux store. However since it's just vanilla Array.map it doesn't return an observable. Which means the observable isn't stored in this.details$, which means it does not render in my template with {{ details$ | async | json }}
How can I achieve this secondary XHR call based upon the results of the first?
ngOnInit() {
this.store.dispatch(new actions.GetAllItems())
this.details$ = this.actions$.pipe(
ofType(actions.types.GetAllItemsSuccess),
mergeMap(() => {
return this.store.select(selectors.getItemsById); // filter to multiple items based on item ID
}),
map((items: models.IItemGeneralResponse[]) => {
items.map(item => { // sync map does not seem like it belongs in rxjs
this.store.dispatch(
new actions.GetItemDetail(item.id)
);
});
})
);
}
You are trying to do ngrx effects stuff in your angular component. Use effects to handle side effects (calls to the backend/fetching data from local storage etc...) and make your component to watch for a piece of your state via a selector. Let's summarize like this -
Your component [or your guard or resolver] will just dispatch an action to the store.
If you set up a reducer for that action then your reducer will be called first otherwise it will go to step 3
In your effect, you are watching for the dispatched action. Your effect will make the first call and then from the response of the first call, it will make the second call and then it will update the state in your store [or piece of the state] which is being watched by your component by dispatching the respective actions to the store.
This is a typical workflow [It may vary as per the need of the app but the basic idea remains the same]. So keeping the basic idea lets modify your code like this -
In your component
sliceOfState$: Observable<any>; //change the type of observabe as per your app
ngOnInit() {
this.store.dispatch(new actions.GetAllItems())
//this observable will be used to render your data on UI
//you can use various rxjs operators to transform your data before shoing to UI
this.sliceOfState$ = this.store.select(//your selector which gives you sliceOfState);
}
Now In your effect -
#Effect()
this.details$ = this.actions$.pipe(
ofType(actions.types.GetAllItems),
switchMap(() => {
//here you call API which makes the call to backend which return allItems
return this.yourServiceWhichGetAllItems.getAllItems();
}),
switchMap(allItems => {
//now for each item you need to get its detail
//so forkJoin all the observables which calls the backedn for each item
const obs$ = allItems.map(item => this.yourServiceWhichGetDetails.getItemDetail(item));
return forkJoin(obs$);
})
map(allItemsWithDetails => {
//here you should call your action which will update the state in your store
return new actions.SetAllItemsDetails(allItemsWithDetails);
})
);
I have provided pseudo code which will give you an idea of how to achieve what you want to do. For more info, you can visit the official site of ngrx - https://ngrx.io/guide/effects
Have kinda a unique question, in my code I have a listener to a database that loads down objects into an array.
All I do when I load it in is
AddObject(obj){
this.setState({
Data: [...this.state.Data, obj]
});
}
Pretty simple. However this listener function, there is no exact time when need data will be added. When I go to use that Data sent in Data, I went to pull it out of the Data Array, however I am worried if I try copying data out of the array, or removing the "seen" data, I will get weird behaivor if my listener function triggers and I try adding data to the array at the same time.
Is there some sort of a way to do this? I guess you could call this a shared resource
Ideally, I would have something like this:
loadDataIN(){
var LengthToGrab = this.state.Data.length;
//we need to remove this length, now any new data will be added to index 0
}
Does this make sense? basically I am trying to figure out the best way to remove data from this array, and not have to worry about overwritting, or losing data. Maybe some sort of processing que
From official doc
setState() enqueues changes to the component state and tells React
that this component and its children need to be re-rendered with the
updated state.
You don't need to worry that two kinds of situation would have conflict in the same time.
setState() enqueues the pending state before the changes be rendered.
In fact, no matter how mechanism be implemented, React is a framework of JavaScript which is working on a model event-loop.
So if you want to pull out the data from this.state.Data:
loadDataIN(){
this.setState(function(prevState, props) {
// this.fetchData = prevState.Data;
return {
Data: []
};
});
}
I have a To-Do List app that creates a TodoItem by dispatching a CREATE_TODO_REQUEST action, which causes a middleware to make aPOST request to an API and respond with CREATE_TODO_SUCCESS with the newly created TodoItem returned by the API. This ToDoItem has a messy hexadecimal ID (like 59e52a5ec8dae14f2420a9ef) assigned to it by our database.
The problem is, sometimes the API could take a few seconds to respond (especially if the user is on a weak connection), so I'd want to optimistically update our application state with the new ToDoItem before the server is done processing it.
This pattern gets messy because all my TodoItems are indexed by ID in my Redux store, and their order is stored in a list of IDs. These IDs are generated by the API after a ToDoItem gets created.
{
byId: {
59e52a5ec8dae14f2420a9ef: {...},
59e52a5ec8dae14f2420a434: {...}
},
ids: [
'59e52a5ec8dae14f2420a9ef',
'59e52a5ec8dae14f2420a434'
]
}
My question is, what ID should I assign my eagerly-created ToDoItem while I wait for the API to return the newly created ToDoItem with a proper ID? Is there an established pattern for handling this type of situation?
I could use a random number generator to create a provisional ID and replace it with the real ID when the CREATE_TODO_SUCCESS action is dispatched (see sample app state below).
{
byId: {
59e52a5ec8dae14f2420a9ef: {...},
59e52a5ec8dae14f2420a434: {...},
"provisional-todo-1": {...} // this is being created on the API rn
},
ids: [
'59e52a5ec8dae14f2420a9ef',
'59e52a5ec8dae14f2420a434',
'provisional-todo-1'
]
}
But this might require some complex logic keeping track of which provisional ToDoItem is associated with actual ToDoItems that are later returned from the server. Additionally, there is the complexity associated with making sure actions dispatched against provisional ToDoItems (marking as complete, editing, deleting) are applied to the correct "real" ToDoItems after they are created.
The easiest answer is to create an local object with a mapping to the remote id.
For example, it might look something like this:
class Todo {
constructor() {
this.id = 'local' + Todo.globalId;
Todo.globalId += 1;
this.remoteId = null;
}
resolve(remoteId) {
this.remoteId = remoteId;
}
}
Todo.globalId = 0;
In redux, you could store these Todo objects, and use those internally to track your state. Then, when the API finally comes back with a value, you can set the remoteId. If there is some failure you could remove the local object or perhaps set a flag.
I'm learning to build a pokemone app that hits the pokemon api, which gives you a lot more info then you need. I'm having trouble figuring out where I should put my code that sifts out the only info I need for my app.
For example, fetching a pokemon's type will return you a type object that looks like this:
{pokemon: [...], weakAgainst: [...]}
If this is what is the data that I'm getting back from my ajax request, and I only want the pokemon array, should
I sift this out in the success of the ajax call that is in my actionsCreators.js and then return that to be dispatched to the store? or
dispatch all of the data type object to my reducer and then in my reducer.js file sift out the pokemon array and copy it to my state? or
copy the whole data object to my state and then sift out the pokemone array in my component's props (that was passed down from mapStateToProps)?
I'm thinking it should be in the reducer? Because it's job is to reduce data to what you need and move it on to the state?
I am of the mind that you should hand the reducer what it needs, if you have the means. Inevitably, you will need to massage and manipulate data in the reducer, but if you can minimize it, I would. Your reducer should be a pure function -- no side effects, the reducers always returns same output given same input etc.. I like to reduce "side effects" by giving the reducer exactly what it needs, especially since the data you need to handoff is so nicely handed to you.. ala.
your actionCreator
export function getPokemanApiData() {
return dispatch => {
axios.get(APIS.getPokemanURL)
.then((response) => {
dispatch(getSuccess(response))
})
.catch((err) => {
dispatch(getError(err))
})
}
}
function getSuccess(response) {
return {
type: TYPES.MY_POKEMAN_TYPE,
payload: response.pokemon
}
}
reducer
case TYPES.MY_POKEMAN_TYPE:
return {
...state,
pokemonData: action.paylod
}
So, the way I see it - try to reduce side effects, minimize the amount of work the reducer has to do. Makes things easier to test too. In the example above, the reducer has exactly what it needs, returns a new state without much fuss. I've read others code in which they like to do the lifting in the reducer, not me. I'd like to hear what others say.