Ngxs not completly waiting for action to resolve successfully when using an action handler - ngxs

after dispatching an action to post images to a backend API, a second action should be triggered. I try to accomplish this by using an action handler that's listening for that postAction to resolve successfully and then should execute the second action. But for some reason the second action get's executed before the first action really resolves successfully.
Does anyone know why?
postaction:
#Action(PostAttachment)
postAttachment(ctx: StateContext<EditEntityStateModel>) {
ctx.patchState({ currentlyUploading: true });
const previews = [...ctx.getState().previews.data];
return previews.forEach((preview, index) => {
//while (ctx.getState().jobsInQueue <= 100) {
previews[index].status = 'uploading';
ctx.patchState({
jobsInQueue: ctx.getState().jobsInQueue + 1,
previews: { ...ctx.getState().previews, data: previews }
});
return this.entityDataService
.postAttachment(preview, ctx.getState().selectedEntityId)
.subscribe(
progress => {
console.log(progress);
},
error => {
const updated = [...ctx.getState().previews.data];
console.error(error);
updated[index].status = 'error';
ctx.setState({
...ctx.getState(),
jobsInQueue: ctx.getState().jobsInQueue - 1,
previews: { ...ctx.getState().previews, data: updated }
});
},
() => {
const updatedAttachment = [...ctx.getState().previews.data][index];
updatedAttachment.status = 'success';
const updatedPreviews = [...ctx.getState().previews.data];
updatedPreviews[index] = updatedAttachment;
ctx.patchState({ previews: { ...ctx.getState().previews, data: updatedPreviews}});
ctx.patchState({
jobsInQueue: ctx.getState().jobsInQueue - 1,
previews: {
...ctx.getState().previews,
data: updatedPreviews.filter((_, position) => position !== index),
showEmptyMessage: !(updatedPreviews.length > 0),
},
currentlyUploading: updatedPreviews.length > 0
})
}
);
//}
});
action handler:
this.actions$.pipe(ofActionSuccessful(PostAttachment)).subscribe(() => {
const selectedEntityId = this.store.selectSnapshot(EditEntityState.selectedEntityId);
this.store.dispatch(new LoadAttachments(selectedEntityId));
})

Related

UseReducer hook doesn't update state (Can't perform a React state update on an unmounted component)

I'm trying to use useReducer instead of useState in a custom hook that loads the initial data from the API, and getting an error updating a state. (I use useReducer here for learning purposes).
The component fetches data firstly correctly, the error occurs when I update the state (book/edit/delete interview).
I left the previous useState code in the comments for better understanding.
import { useReducer, useEffect } from "react";
import axios from "axios";
const SET_DAY = "SET_DAY";
const SET_APPLICATION_DATA = "SET_APPLICATION_DATA";
const SET_INTERVIEW = "SET_INTERVIEW";
const reducer = (state, action) => {
switch (action.type) {
case SET_DAY:
return { ...state, day: action.day }
case SET_APPLICATION_DATA:
return {
...state,
days: action.days,
appointments: action.appointments,
interviewers: action.interviewers
}
case SET_INTERVIEW: {
return { ...state, id: action.id, interview: action.interview }
}
default:
throw new Error();
}
}
export default function useApplicationData() {
// const [state, setState] = useState({
// day: "Monday",
// days: [],
// appointments: {},
// interviewers: {}
// });
const initialState = {
day: "Monday",
days: [],
appointments: {},
interviewers: {}
};
const [state, dispatch] = useReducer(reducer, initialState);
//updates the spots remaining when book/edit/cancel interview
const updateSpots = (requestType) => {
const days = state.days.map(day => {
if(day.name === state.day) {
if (requestType === 'bookInterview') {
// return { ...day, spots: day.spots - 1 }
return dispatch({ type: SET_DAY, spots: day.spots - 1 });
}else {
// return { ...day, spots: day.spots + 1 }
return dispatch({ type: SET_DAY, spots: day.spots + 1 });
}
}
// return { ...day };
return dispatch({ type: SET_DAY, spots: day.spots });
});
return days;
}
//sets the current day data
// const setDay = day => setState(prev => ({ ...prev, day }));
const setDay = (day) => dispatch({ type: SET_DAY, day });
//adds new interview data to database
const bookInterview = (id, interview) => {
const appointment = { ...state.appointments[id] };
const bookOrEdit = appointment.interview ? 'edit' : 'book'; //defines the request type
appointment.interview = { ...interview };
const appointments = { ...state.appointments, [id]: appointment };
let days = state.days;
if (bookOrEdit === 'book') {
days = updateSpots('bookInterview');
}
return axios
.put(`/api/appointments/${id}`, {interview})
.then(() => {
//setState({ ...state, appointments, days });
dispatch({ type: SET_INTERVIEW, id, interview });
})
};
//deletes interview data from database
const cancelInterview = (id) => {
const appointment = {...state.appointments[id], interview: null};
const appointments = {...state.appointments, [id]: appointment };
const days = updateSpots();
return axios
.delete(`/api/appointments/${id}`)
.then(() => {
//setState({ ...state, appointments, days });
dispatch({ type: SET_INTERVIEW, id, interview: null });
})
};
useEffect(() => {
let isMounted = false;
Promise.all([
axios.get('/api/days'),
axios.get('/api/appointments'),
axios.get('/api/interviewers')
])
.then((all) => {
// setState(prev => ({
// ...prev,
// days: all[0].data,
// appointments: all[1].data,
// interviewers: all[2].data}));
// });
if (!isMounted) {
console.log("done!");
}
isMounted = true;
dispatch({ type: SET_APPLICATION_DATA, days: all[0].data, appointments: all[1].data, interviewers:all[2].data });
});
}, []);
return { state, setDay, bookInterview, cancelInterview }
};
I'd be appreciated for pointing me in the right direction on what I'm doing wrong. Thank you!

Call data after refreshing the page (React Native Hooks)

Here's my code first
const [getData, setGetData] = useState();
const [ref, setRef] = useState();
const initializeData = async() => {
const userToken = await AsyncStorage.getItem('user_id');
setGetData(JSON.parse(userToken));
}
useEffect(() => {
return initializeData();
},[])
useEffect(() => {
let interval;
if(getData != null)
{
interval = setInterval(() => {
setRef(firestore().collection('**********').where("SendersNo", "==", getData.number));
}, 2000);
}
return () => clearInterval(interval);
},[getData])
useEffect(() => {
if(ref != null)
{
return ref.onSnapshot(querySnapshot => {
const list = [];
querySnapshot.forEach(doc => {
const {
id,driverName,driverContactNumber,driverRating,driverPlateNumber,driverTrackingNumber,userPlaceName,
destinationPlaceName,PaymentMethod,Fare
} = doc.data();
list.push({id: doc.id,driverName,driverContactNumber,driverRating,
driverPlateNumber,driverTrackingNumber,userPlaceName,destinationPlaceName,PaymentMethod,Fare});
});
setUserBookingData(list);
console.log("HEY!");
});
}
},[])
const CurrentTransaction = () => {
if(ref == null)
{
return (
<View>
<Text>You don't have a Current Transaction</Text>
</View>
)
}
else
{
return userBookingData.map((element) => {
return (
<View key={element.id}>
<View>
<Text>{element.name}</Text>
</View>
</View>
)
});
}
}
So currently right now what I am trying to is if there's a data on my firestore it will update on the screen but before updating it I need to get the data from the setGetData so that I can query it but the problem is that when I refresh the whole simulator/page it doesn't get the data but instead just a blank page . But when i edit and save my code without refreshing the page/simulator it can get the data . Can someone help me what I am doing wrong .
EDIT
if I do this
useEffect(() => {
if(ref != null)
{
return ref.onSnapshot(querySnapshot => {
const list = [];
querySnapshot.forEach(doc => {
const {
id,driverName,driverContactNumber,driverRating,driverPlateNumber,driverTrackingNumber,userPlaceName,
destinationPlaceName,PaymentMethod,Fare
} = doc.data();
list.push({id: doc.id,driverName,driverContactNumber,driverRating,
driverPlateNumber,driverTrackingNumber,userPlaceName,destinationPlaceName,PaymentMethod,Fare});
});
setUserBookingData(list);
console.log("HEY!");
});
}
else
{
return null;
}
},[ref])
it keeps looping the console.log('hey') but it can get the data and display it . but it loops so its bad.
i believe snapshot from firebase realtime database is a listener so its doesn't need setinterval
useEffect(() => {
if(getData != null)
{
const ref = firestore().collection('**********').where("SendersNo", "==", getData.number);
ref.onSnapshot(querySnapshot => {
const list = [];
querySnapshot.forEach(doc => {
const {
id,driverName,driverContactNumber,driverRating,driverPlateNumber,driverTrackingNumber,userPlaceName,
destinationPlaceName,PaymentMethod,Fare
} = doc.data();
list.push({id: doc.id,driverName,driverContactNumber,driverRating,
driverPlateNumber,driverTrackingNumber,userPlaceName,destinationPlaceName,PaymentMethod,Fare});
});
setUserBookingData(list);
console.log("HEY!");
});
}
return () => {
//clear your ref listener here
}
},[getData])
if you put a return on use effect it will be called after the screen is no longer used.
useEffect(()=>{
//inside this will be called when the screen complete render
const someListener = DeviceEventEmitter('listentosomething',()=>{
//do something
});
return ()=>{
//inside this will be called after the screen no longer be used
//example go to other screen
someListener.remove();
}
},)

async await with elasticsearch search/scroll

I'm using await as part of my search but as its +30000 items I have to use scroll.
The issue is that the initial part of the search is complete before the scroll is so the await fires and function carries on. what should I be doing to stop this?
var allTitles = [];
try {
await client.search({
index: 'myindex',
scroll: '30s',
source: ['title'],
q: 'title:test'
}, function getMoreUntilDone(error, response) {
response.hits.hits.forEach(function (hit) {
allTitles.push(hit._source.title);
});
if (response.hits.total > allTitles.length) {
client.scroll({
scrollId: response._scroll_id,
scroll: '30s'
}, getMoreUntilDone);
} else {
console.log('every "test" title', allTitles);
}
});
} catch (err) {
console.log(err)
}
SO, I've re-written it so here it is to help anyone else who wants it.
var stuff = []
const q = {params}
const searchstuff = (q) => {
return new Promise((resolve, reject) => {
const get = x => {
stuff = stuff.concat(x.hits.hits)
if (x.hits.total > stuff.length) {
this.client.scroll({ scrollId: x._scrollId, scroll: '10s'}).then(get)
} else {
resolve(stuff)
}
}
this.client.search(q).then(get).catch(reject)
})
}
const search = await searchstuff(q)
if (search) console.log('Searched')

I need changed values on handclick from the Edit Form using custom action. How can I get it?

In the code below I want to get the form values from Edit form and sent using fetch on handleClick.
class GenerateButton extends Component {
handleClick = () => {
const { push, record, showNotification, values } = this.props;
const updatedRecord = { ...record, is_approved: true };
fetch(`api/reports/${record.id}`, { method: 'GET', body: updatedRecord })
.then((response) => {
return response.blob();
}).then(function(blob) {
console.log(blob);
})
.catch((e) => {
showNotification('Error: report generation failed.', 'warning')
});
}
render() {
return <RaisedButton label="Generate" onClick={this.handleClick} />;
}
}

After closing the modal dialog refresh the base view

suggestion and code sample
I am new to Backbone marionette, I have a view ("JoinCommunityNonmemberWidgetview.js") which opens a modal dialog ("JoinCommunityDetailWidgetview.js").On closing of the dialog ( I want the view JoinCommunityNonmemberWidgetview.js") to be refreshed again by calling a specific function "submitsuccess" of the view JoinCommunityNonmemberWidgetview.js.
How can I achieve it.
The code for the modal is as below:
define(
[
"grads",
"views/base/forms/BaseFormLayout",
"models/MembershipRequestModel",
"require.text!templates/communitypagewidget/JoinCommunityWidgetDetailTemplate.htm",
],
function (grads, BaseFormLayout, MembershipRequestModel, JoinCommunityWidgetDetailTemplate) {
// Create custom bindings for edit form
var MemberDetailbindings = {
'[name="firstname"]': 'FirstName',
'[name="lastname"]': 'LastName',
'[name="organization"]': 'InstitutionName',
'[name="email"]': 'Email'
};
var Detailview = BaseFormLayout.extend({
formViewOptions: {
template: JoinCommunityWidgetDetailTemplate,
bindings: MemberDetailbindings,
labels: {
'InstitutionName': "Organization"
},
validation: {
'Email': function (value) {
var emailconf = this.attributes.conf;
if (value != emailconf) {
return 'Email message and Confirm email meassage should match';
}
}
}
},
editViewOptions: {
viewEvents: {
"after:render": function () {
var self = this;
var btn = this.$el.find('#buttonSubmit');
$j(btn).button();
}
}
},
showToolbar: false,
editMode: true,
events: {
"click [data-name='buttonSubmit']": "handleSubmitButton"
},
beforeInitialize: function (options) {
this.model = new MembershipRequestModel({ CommunityId: this.options.communityId, MembershipRequestStatusTypeId: 1, RequestDate: new Date() });
},
onRender: function () {
BaseFormLayout.prototype.onRender.call(this)
},
handleSubmitButton: function (event) {
this.hideErrors();
// this.model.set({ conf: 'conf' });
this.model.set({ conf: this.$el.find('#confirmemail-textbox').val() });
//this.form.currentView.save();
//console.log(this.form);
this.model.save({}, {
success: this.saveSuccess.bind(this),
error: this.saveError.bind(this),
wait: true
});
},
saveSuccess: function (model, response) {
var mesg = 'You have submitted a request to join this community.';
$j('<div>').html(mesg).dialog({
title: 'Success',
buttons: {
OK: function () {
$j(this).dialog('close');
}
}
});
grads.modal.close();
},
saveError: function (model, response) {
var msg = 'There was a problem. The request could not be processed.Please try again.';
$j('<div>').html(msg).dialog({
title: 'Error',
buttons: {
OK: function () {
$j(this).dialog('close');
}
}
});
}
});
return Detailview;
}
);
I would use Marionette's event framework.
Take a look at: https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.commands.md
Specifically, you need to:
1) Create a marionette application :
App = new Marionette.Application();
2) Use the application to set up event handlers
//Should be somewhere you can perform the logic you are after
App.commands.setHandler('refresh');
3) Fire a 'command' and let marionette route the event
App.execute('refresh');

Resources