Reputation: 31
In the following store I try to upload some photos and want to update the UI with the progress. This is my main store:
const Store = types
.model({
photos: types.array(Photo),
isLoading: false,
})
.actions((self) => {
return {
addPhoto(url: string) {
console.log("Add photo: ", url);
const photo = Photo.create({ url });
self.photos.push(photo);
},
send: flow(function* () {
self.isLoading = true;
console.log("Uploading photos...");
for (let i = 0; i < self.photos.length; i += 1) {
yield self.photos[i].upload();
}
console.log("Completed");
self.isLoading = false;
}),
};
});
But when I trigger the send action I get the error:
[mobx-state-tree] Cannot modify 'AnonymousModel@/photos/0', the object is protected and can only be modified by using an action.
This is the Photo model:
const Photo = types
.model({
url: types.string,
progress: types.optional(types.number, 0),
isLoading: false,
})
.actions((self) => {
const dummyUpload = (duration: number, callback?: Callback) =>
new Promise((resolve) => {
let progress = 0;
const interval = setInterval(() => {
progress += 0.2;
callback?.(progress);
if (progress >= 1) {
clearInterval(interval);
resolve(undefined);
}
}, duration / 5);
});
return {
upload: flow(function* () {
self.isLoading = true;
yield dummyUpload(3000, (progress) => {
self.progress = progress;
});
self.isLoading = false;
}),
};
});
Note: for now I have a dummy upload action with a progress callback.
I don't understand why the error states that the store is modified outside an action because I believe it is performed inside the Photo.upload action.
Can someone please tell me what's wrong with above code?
Here is a codesandbox to see it in action: https://codesandbox.io/s/mutable-pine-ukm5sv
Upvotes: 3
Views: 663
Reputation: 112807
dummyUpload
will be handled correctly by using yield
, but the progress callback function will not.
You could e.g. create a separate actions
block with the progress callback to make it an action and it will work as expected:
const Photo = types
.model({
url: types.string,
progress: types.optional(types.number, 0),
isLoading: false
})
.actions((self) => ({
setProgress(progress: number) {
self.progress = progress;
}
}))
.actions((self) => {
// ...
return {
upload: flow(function* () {
self.isLoading = true;
yield dummyUpload(3000, self.setProgress);
self.isLoading = false;
})
};
});
Upvotes: 1