I have implemented the socket, which was not able to established after pressing F5 button,
when users logged into the app, socket has been establishes successfully, when I hit F5, page refreshes but socketConnection is not invoked because props is undefined due to which conditional statement has been failed to execute.
After inspecting I have found that, I invoked the main socket creation function inside App.js, it takes props as as argument and one conditional statement. I have found that the props itself is undefined due to which the socket i not able t re-established.
App.js
import React, { useEffect } from 'react';
import './App.scss';
import Routes from './Routes';
import { connect } from 'react-redux';
import socketConnection from '#Hoc/SocketComponent';
const App = () => {
useEffect((props) => {
if (props?.session?.user?.extraDetails?.extensionNo) {
socketConnection(props);
}
}, []);
return (
<div>
<Routes />
</div>
);
};
const mapStateToProps = (state) => ({
session: state.session
});
export default connect(mapStateToProps)(App);
SocketComponent
export default function socketConnection(socketEvent) {
if (!window?.socket)
sessionService.loadUser().then((currentUser) => {
if (
currentUser?.extraDetails?.extensionNo &&
currentUser?.params?.AgentID
)
socket = window.socket = io(REACT_APP_SOCKET_URL, {
query: {
agentId: currentUser.params.AgentID,
extensionNo: currentUser.extraDetails.extensionNo,
},
});
socket.on("connect", (data) => {
console.info(data);
});
socket.on("EventEstablished", (data) => {
eventEstablished(data, currentUser, socketEvent);
});
socket.on("EventAgentLogout", () => {
notification.error({
message: "Softphone loggedout, please re-login",
duration: 0,
});
socketEvent.history.push("/home");
});
socket.on("EventPropertiesChanged", (data) => {
manualGeocode(data);
});
socket.on("EventAttachedDataChanged", (data) => {
if (data.data?.incidentNumber) {
store.dispatch({
type: SET_LIVE_CALL_DATA,
payLoad: { psapReferenceId: data?.data?.incidentNumber },
});
}
if (data.data?.updateLocation && data.data?.isRetransmit) {
let functionCall = "retransmit" ;
// if (data.data?.isRetransmit) {
// functionCall = "retransmit" ;
// }
getCallData( functionCall );
}
// manualGeocode(data);
});
socket.on("EventDestinationBusy", (data) => {
console.log("EventDestinationBusy", data);
});
socket.on("EventAbandoned", (data) => {
notification.error({
message: "Call abandoned",
description: "The caller abandoned the call before it was answered",
duration: 0,
});
});
socket.on("EventDNOutOfService", (data) => {
notification.error({
message: "Extension Out of Service !",
description:
"This extension is out of service and cannot make or receive calls. ",
duration: 0,
});
});
socket.on("EventAgentReady", (data) => {
console.log("EventAgentReady", data);
});
socket.on("EventAgentNotReady", (data) => {
console.log("EventAgentNotReady", data);
});
socket.on("EventReleased", (data) => {
eventReleased(data);
});
socket.on("EventPartyDeleted", (data) => {
eventPartyDeleted(data);
});
socket.on("EventInvite", (data) => {
console.log(data);
if (
!store?.getState()?.secondAgent?.secondAgent?.isSecondAgent &&
socketEvent.history.location.pathname === "/waiting"
) {
eventInvite(data, currentUser, socketEvent);
}
});
socket.on("disconnect", (data) => {
console.log(data);
});
socket.on("workflow", (data) => {
store.dispatch({ type: SET_WORKFLOW_DATA, payLoad: data });
});
socket.on("workflowUpdatedComponent", (data) => {
store.dispatch({ type: SET_WORKFLOW_OPTIONS, payLoad: data });
});
socket.on("gather-info", (data) => {
console.log(data);
if (data?.data?.extensionNo != currentUser.extraDetails.extensionNo) {
store.dispatch({
type: SET_GATHER_INFO,
payLoad: data?.data?.gatherInfo,
});
}
});
socket.on("geoCodeSession", (data) => {
console.log(data);
if (data?.data?.extensionNo != currentUser.extraDetails.extensionNo) {
if (data?.data?.updateLocation) {
store.dispatch({
type: SET_MANUAL_GEOCODE,
payLoad: { updateLocation: true },
});
}
// let timeFrame = new Date(data.data.timestamp);
// timestamp = timeFrame.toLocaleDateString() + ' ' + timeFrame.toLocaleTimeString();
store.dispatch({
type: SET_MANUAL_GEOCODE,
payLoad: {
status: true,
latitude: data?.data?.latitude,
longitude: data?.data?.longitude,
address: data?.data?.address,
country: data?.data?.country,
region: data?.data?.region,
timestamp: data?.data?.timestamp,
},
});
}
});
socket.on("EventSessionInfo", (data) => {
if (data?.data?.extensionNo !== currentUser?.extraDetails.extensionNo) {
if (data.data.sessionStatus === "Over") {
store.dispatch({
type: SET_SECOND_AGENT,
payLoad: { status: "Disconnected", isSecondAgent: false },
});
} else if (
data.data.sessionStatus === "Alive" &&
data.data.agentNameChat
) {
store.dispatch({
type: SET_SECOND_AGENT,
payLoad: {
isSecondAgent: true,
status: "Connected",
anotherAgent: data.data.messageText,
isSecondAgent: true,
},
});
} else if (
data.data.sessionStatus === "Alive" &&
!data.data.messageText.includes("Leaving ChatRoom..") &&
!data.data.messageText.includes("Join Chat Session")
) {
chatStore(data);
}
}
});
});
}
I am not able to figured it out what went wrong, however I am trying to load when props are there but not able to do the same.
PROBLEM
It seems like props are not loaded while execution of `useEffect
Solution might be require some kind of delay to useEffect, so once props properly loaded form localstorage
Related
I have a thunk which fetches data from firestore and maps doc IDs to each doc. For some reason it's fulfilled and returns undefined before the mapping finishes. I can verify this because the log before return statement appears a second or two after the fulfilled reducer logs.
My thunk:
export const fetchNotesByCustomerId = createAsyncThunk(
'fetchNotesByCustomerId',
async (custId, { getState, dispatch }) => {
const customerId = custId;
if (customerId !== undefined) {
notesService
.fetchNotesByCustomerId(customerId)
.then((snapshot) => {
if (snapshot.docs.length !== 0) {
const notes = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id
} as INote)
);
console.log('notes inside thunk: ', notes); // This shows after the fulfilled reducer logs
return {
error: false,
data: notes
};
} else
return {
error: true,
message: 'No notes found in firebase',
data: []
};
})
.catch((error) => {
return { error: true, message: error.message, data: [] };
});
} else return { error: true, message: 'No Customer Id' };
}
);
I know .map is an async function that returns an array of promises, but when I use await, intellisense notifies me that it makes no difference on the behavior of the function.
So as an alternative, I tried to resolve the array of promises like this, but saw no difference:
.then(async (snapshot) => {
if (snapshot.docs.length !== 0) {
const notesPromisesArray = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id
} as INote)
);
await Promise.all(notesPromisesArray).then((notes) => {
console.log('notes inside thunk: ', notes);
return {
error: false,
data: notes
};
});
} else
return {
error: true,
message: 'No notes found in firebase',
data: []
};
})
How can I get this .map to return before the thunk is fulfilled?
The problem you're facing has nothing to do with Redux Async Thunk but basic JavaScript only.
In the if condition that you have:
if (customerId !== undefined) {
notesService
.fetchNotesByCustomerId(customerId)
.then((snapshot) => {
if (snapshot.docs.length !== 0) {
const notes = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id,
} as INote)
)
console.log('notes inside thunk: ', notes) // This shows after the fulfilled reducer logs
return {
error: false,
data: notes,
}
} else
return {
error: true,
message: 'No notes found in firebase',
data: [],
}
})
.catch((error) => {
return { error: true, message: error.message, data: [] }
})
}
You're using noteService.fetchNotesByCustomerId() which is an async function.
When the execution goes to this block, since JavaScript Event loop will forward the async function to its thread pool, it goes to the next step without even the execution of noteService.fetchNotesByCustomerId() getting over and resolves the thunk without returning anything.
You can easily resolve this by adding a return statement next to your call:
export const fetchNotesByCustomerId = createAsyncThunk('fetchNotesByCustomerId', async (custId, { getState, dispatch }) => {
const customerId = custId
if (customerId !== undefined) {
return notesService
.fetchNotesByCustomerId(customerId)
.then((snapshot) => {
if (snapshot.docs.length !== 0) {
const notes = snapshot.docs.map(
(doc) =>
({
...doc.data(),
docId: doc.id,
} as INote)
)
console.log('notes inside thunk: ', notes) // This shows after the fulfilled reducer logs
return {
error: false,
data: notes,
}
} else
return {
error: true,
message: 'No notes found in firebase',
data: [],
}
})
.catch((error) => {
return { error: true, message: error.message, data: [] }
})
} else return { error: true, message: 'No Customer Id' }
})
How can I catch the errors from async method in the thunk into the Functional Components?
For example I have the following thunk:
export const updateCostCenter = (data: Record<string, unknown>) => async (dispatch: Dispatch<IWorkforceState>) => {
dispatch(requestUpdateCostCenter());
return api('put', `${costCenterUrl}/${data.rowId}`, data)
.then(response => {
return dispatch(receiveUpdateCostCenter(response.data));
})
.catch(err => {
return dispatch(errorUpdateCostCenter(err.response?.data?.description));
});
};
and in the functional component the following asynchronous method that calls the thunk:
props.updateCostCenter(valueToSubmit).then(
() => {
props.showToastNotification('success', 'Successful', props.translate('cost_center_successfully_updated'));
AmplitudeService.logEvent(props.translate('edit_cost_center'));
props.hideDialog();
resetForm();
setSubmitting(false);
if (props.loadData) {
props.loadData();
}
return
}
).catch(() => {
props.showToastNotification('error', 'Error', props.translate('cost_center_update_error'))
});
Unfortunately, I don't know why in case of error it doesn't enter into the catch from the functional component. I tried to add throw TypeError() after the dispatch of the error action, it works, but the thunk unit test fails on the pipeline.
This are the tests:
it('update cost center success', function() {
mockAdd.mockImplementation(
() =>
Promise.resolve({
data: costCenter,
} as any)
);
const expectedActions = [
{ type: WorkforceActions.REQUEST_UPDATE_COST_CENTER },
{ type: WorkforceActions.RECEIVE_UPDATE_COST_CENTER, costCenter },
];
store.dispatch(updateCostCenter({ data: costCenter }) as any).then(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(api).toHaveBeenCalled();
return
}).catch((unexpectedErr: any) => console.log(`Unexpectedly rejected promise ${unexpectedErr}`));
});
it('update cost center error', function() {
mockAdd.mockImplementation(
() =>
Promise.reject({
response: { data: { description: 'dummy-message' } },
} as any)
);
const expectedActions = [
{ type: WorkforceActions.REQUEST_UPDATE_COST_CENTER },
{ type: WorkforceActions.ERROR_UPDATE_COST_CENTER, message: 'dummy-message' },
];
store.dispatch(updateCostCenter({ data: costCenter }) as any).catch(() => {
expect(store.getActions()).toEqual(expectedActions);
expect(api).toHaveBeenCalled();
});
});
Because you don't return an error.
export const updateCostCenter = (data: Record<string, unknown>) => async (dispatch: Dispatch<IWorkforceState>) => {
dispatch(requestUpdateCostCenter());
return api('put', `${costCenterUrl}/${data.rowId}`, data)
.then(response => {
dispatch(receiveUpdateCostCenter(response.data));
return response;
})
.catch(err => {
dispatch(errorUpdateCostCenter(err.response?.data?.description));
throw err; // or throw new Error();
});
};
I am using React/Redux.
The main issue is that when i use Promise then component is not re-rendered, whereas the code is working fine when promise code is not used.
Action Creator
const updateColor = colorobj => {
return dispatch =>
new Promise(function(resolve, reject) {
dispatch(fetchColorBegin());
axios
.post(config.APIURL.color.update, colorobj)
.then(response => {
const data = response.data;
if (data.errorno !== 0) {
dispatch(fetchColorFailure(data.errormsg));
reject(data.errormsg);
} else {
dispatch(updateColorSuccess(colorobj));
resolve('Color Updated');
}
})
.catch(error => {
dispatch(fetchColorFailure(error.message));
reject(error.message);
});
});
};
Reducer
case UPDATE_COLOR_SUCCESS:
const todoIndex = state.data.findIndex(todo => todo.id === action.payload.id);
return update(state, {
loading: { $set: false },
data: { [todoIndex]: { $merge: action.payload } },
error: { $set: null}
});
Component
the state is updated but the component is not updated.
const handleEditOk = values => {
let colorobj = {
id: state.updateData.id,
colorname: values.colorname,
colorcode: values.colorcode,
};
dispatch(updateColor(colorobj))
.then(response => {
message.success(response);
onCancel();
})
.catch(error => {
message.error(error);
});
};
The component update itself only on commenting the promise code.
The problem now is that it is not showing success/failure message.
const handleEditOk = values => {
let colorobj = {
id: state.updateData.id,
colorname: values.colorname,
colorcode: values.colorcode,
};
dispatch(updateColor(colorobj))
// .then(response => {
// message.success(response);
// onCancel();
// })
// .catch(error => {
// message.error(error);
// });
};
Kindly suggest.
I am trying to create a context api for twilio call connection. I would like to take help from twilio experts if I am doing it in correct way or not. In this context, What I am trying to do is connect twilio device and if the token expires then reconnect. But this way, the outgoing call is not working yet I could see the device is ready printed on my console. When I try to call, the deviceInstance state is shown null.
This is how I am doing
import { Device, Connection } from 'twilio-client';
import twilioReducers from './twilioReducers';
interface ITwilioContext {
// device?: Device;
setDeviceOnline?: () => void;
state: InitialStateType;
dispatch: React.Dispatch<any>;
handleDeviceOutgoing: (params: OutgoingProps) => void;
}
export const TwilioContext = createContext<ITwilioContext>({});
const TwilioProvider = ({ children }: { children: React.ReactNode }) => {
const [deviceInstance, setDeviceInstance] = useState<Device | null>(null);
const [activeWorkspaceId] = useLocalStorage('activeWorkspaceId', null);
const { t } = useTranslation();
const [getVoiceToken, { data }] = useLazyQuery(VOICE_TOKEN, {
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
onError: err => console.error(err),
});
// Commented inorder to prevent reintialization
// const device = new Device();
const initialState = { direction: '', showPhoneWidget: false };
const [state, dispatch] = useReducer(twilioReducers, initialState);
/* event handlers for twilio device */
const handleDeviceReady = (device: Device) => {
console.log('Device ready');
setDeviceInstance(device);
};
const handleDeviceOffline = async (device: Device) => {
console.log('Device offline', device);
if (device.token) {
const twilioTokenDecoded = jwtDecode<JwtPayload>(device.token);
if ((twilioTokenDecoded as any).exp <= Date.now() / 1000) {
await getVoiceToken({});
console.log('twilio new token data', data);
}
}
};
const handleDeviceError = (error: Connection.Error) => {
console.log('Device error', error);
if (TWILIO_ERRORS[error.code] !== undefined) {
ToastMessage({
content: t(TWILIO_ERRORS[error.code].errorKey, TWILIO_ERRORS[error.code].message),
type: 'danger',
});
}
};
/* ----------------------------- */
/* handle incoming calls */
const handleDeviceIncoming = (connection: Connection) => {
console.log('incoming', connection);
dispatch({
type: ACTIONS.INCOMING_CALL,
data: connection,
});
connection.on(deviceEvent.CANCEL, () => {
console.log('incoming-cancel');
dispatch({
type: ACTIONS.INCOMING_CALL_CANCEL,
});
});
connection.on(deviceEvent.DISCONNECT, () => {
console.log('incoming-disconnect');
dispatch({
type: ACTIONS.INCOMING_CALL_DISCONNECT,
});
});
connection.on(deviceEvent.ACCEPT, () => {
console.log('incoming-call-accept');
dispatch({
type: ACTIONS.ANSWER_INCOMING_CALL,
});
});
connection.on(deviceEvent.REJECT, () => {
console.log('incoming-call-reject');
dispatch({
type: ACTIONS.REJECT_INCOMING_CALL,
updateConversationStatus: true,
});
});
connection.on(deviceEvent.ERROR, (err: Connection.Error) => {
console.log('Connection error occured', err);
dispatch({
type: ACTIONS.INCOMING_CALL_ERROR,
status: 'error',
});
});
};
/* ----------------------------- */
/* handle outgoing calls */
const handleDeviceOutgoing = (params: OutgoingProps) => {
if (deviceInstance) {
if (deviceInstance.isInitialized || deviceInstance.status() !== 'ready') {
ToastMessage({ content: t('error.deviceSetup', 'Device is offline.'), type: 'danger' });
return;
}
const connection = deviceInstance.connect(params); // copied from premvp
dispatch({
type: ACTIONS.OUTGOING_CALL_INITIATED,
data: connection,
status: 'connecting',
channelId: params?.channel_sid,
});
connection.on(deviceEvent.RINGING, (val: boolean) => {
if (val) {
dispatch({
type: ACTIONS.OUTGOING_CALL_RINGING,
});
}
});
connection.on(deviceEvent.CANCEL, () => {
console.log('Connection cancelled');
dispatch({
type: ACTIONS.OUTGOING_CALL_DISCONNECT,
});
});
connection.on(deviceEvent.DISCONNECT, (conn: Connection) => {
// handle user hungup
console.log('Connection disconnected', conn);
dispatch({
type: ACTIONS.OUTGOING_CALL_DISCONNECT,
});
});
connection.on(deviceEvent.ACCEPT, (conn: Connection) => {
console.log('Connected to the user', conn); // handle user answercall
dispatch({
type: ACTIONS.OUTGOING_CALL_ANSWERED,
});
});
connection.on(deviceEvent.REJECT, (conn: Connection) => {
console.log('Rejected', conn); // handle user answercall
dispatch({
type: ACTIONS.REJECT_OUTGOING_CALL,
});
});
connection.on(deviceEvent.ERROR, (err: Connection.Error) => {
console.log('Connection error occured', err);
});
} else {
console.log('No Device Instance exist');
}
};
/* ----------------------------- */
useEffect(() => {
const device = new Device();
console.log('device', device, data);
if (data?.getVoiceToken?.data?.voiceToken) {
device.setup(data?.getVoiceToken?.data?.voiceToken, deviceConfig);
device.on(deviceEvent.READY, handleDeviceReady);
device.on(deviceEvent.OFFLINE, handleDeviceOffline);
device.on(deviceEvent.ERROR, handleDeviceError);
device.on(deviceEvent.INCOMING, handleDeviceIncoming);
}
return () => {
device.destroy();
setDeviceInstance(null);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data?.getVoiceToken?.data?.voiceToken]);
useEffect(() => {
if (activeWorkspaceId !== '') {
getVoiceToken({});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeWorkspaceId]);
const value = useMemo(() => {
return {
state,
dispatch,
deviceInstance,
handleDeviceOutgoing,
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [state]);
return <TwilioContext.Provider value={value}>{children}</TwilioContext.Provider>;
};
export default TwilioProvider;
Problem :
I am developing an application using react and redux logic.From a component I am calling for an action. Through the action it is triggering a logic. Here I am providing how I organized my logic.
createLogic({
type: NodeHeirarchyTypes.NODE_HEIRARCHY_ADD,
latest: true,
//debounce: 2000,
process({ MockHTTPClient, getState, action }, dispatch, done) {
//console.log(action.payload.code, action.payload.name);
debugger;
console.log("Calling NODE_HEIRARCHY_ADD ");
var node = {};
node.code = action.payload.code;
node.name = action.payload.name;
node.nodeType = action.payload.nodeType;
node.order = action.payload.order;
console.log("node object is", node);
dispatch(NodeHeirarchyActions.queryStart());
//To load the screen
let HTTPClient;
if (MockHTTPClient) {
HTTPClient = MockHTTPClient;
console.log("Hi I am mock");
} else {
HTTPClient = API;
}
// debugger;
//dispatch(NodeHeirarchyActions.addHeirarchySuccess("node"));
setTimeout(() => {
dispatch(NodeHeirarchyActions.addHeirarchySuccess("node"));
done();
}, 2000);
return;
HTTPClient.Post(endPoints.NODE_HEIRARCHY_CREATION, action.payload)
.then(resp => resp.data)
.then(data => dispatch(NodeHeirarchyActions.addHeirarchySuccess(data)))
.then(data => dispatch(NodeHeirarchyActions.queryEnd(data)))
.catch(err => {
var errorMessage =
(err.response && err.response.data && err.response.data.message) ||
err.message ||
"Node Heirarchy Edit Failed";
console.error(err);
if (err && err.code == "ECONNABORTED") {
errorMessage = "Please check your internet connection.";
}
dispatch(
NodeHeirarchyActions.addFailed({
title: "Error",
message: errorMessage
})
);
dispatch(NodeHeirarchyActions.queryEnd(err));
done();
})
.then(() => done());
}
}),
This is my action.
handleSubmit = event => {
debugger;
this.props.addHeirarchy({
code: this.state.code,
nodeType: this.state.nodeType,
name: this.state.name,
order: this.state.order
});
};
Instead of using payload,when I tried to assign a value directly, it was grabbing data properly
eg:
node.code = "123";
node.name = "Jack";
How can I create a functioning payload?