My goal is to not duplicate onStompErrorCallback
callback.
ps: I am working on another solution, I think it would work.
TL:DR
The current solution is to write onStopErrorCallback
directly in React Hook.
function LoginScreen() {
const dispatch = useDispatch();
const onStopErrorCallback = (receipt: IFrame) => {
dispatch(doSomething());
}
}
However this poses problem, because there are 2 scenarios:
When a user login, e.g in a component called
LoginScreen
there is a method calledlogin
. Thelogin
's method do 2 things:- Send POST request to an API and get a token as response.
- Do the
WebSocket handshake
, and thenStompCommand.CONNECT
,StompCommand.SUBSCRIBE
.
If the user press the
login button
, it will invoke the method.When a user open the app:
- The
App
component will send a GET request to authenticate the token. If the token is not expired, it will do theWebSocket handshake
, and thenStompCommand.CONNECT
, andStompCommand.SUBSCRIBE
.
- The
The problem is:
- Each scenario needs access to the
onStompErrorCallback
, and I can't put it anywhere without duplicating the codeincrease code smell
since it dependsdispatch(doSomething())
.
What I've tried:
I tried to store
new Client(stompConfig: StompConfig)
to redux.Redux can't store
onStopErrorCallback: () => void
, or thenew Client(stompConfig: StompConfig)
. It will throw an error.I tried to use custom hook, e.g
useStompClient(X_Auth_Token: string)
.Each time a component or screen call
useStompClient(X_Auth_Token: string)
, it creates newstompClient
instead of using the existingstompClient
.I tried to use a thunk to the
onStompErrorCallback
The
stompClient
can't connect / subscribe / send message.const onStompErrorCallback = (receipt: IFrame): AppThunk => dispatch => { dispatch(doSomething()); }
Edit
This is the best I can do with custom hook. This is not a solution, the useStompClient
see stompClient as null although it's not, and then it's re-render (this time it see stompClient as not null).
let stompClient: Client | null = null;
function useStompClient(X_Auth_Token: string) {
const dispatch = useDispatch();
if (stompClient === null && X_Auth_Token !== '') {
const onConnectCallback = () => {
console.log('STOMP: connecting');
stompClient?.subscribe('/user/queue/messages', (message) => {
const payload = JSON.parse(message.body);
console.log(payload);
});
};
const onStompErrorCallback = (receipt: IFrame) => {
if (receipt.headers.message.endsWith('Access is denied')) {
stompClient?.deactivate();
dispatch(eraseCredentials());
}
};
const stompConfig: StompConfig = {
brokerURL: `${environment.wsBaseURL}/chat`,
forceBinaryWSFrames: true,
appendMissingNULLonIncoming: true,
connectHeaders: {
'X-Auth-Token': X_Auth_Token,
},
onConnect: onConnectCallback,
onStompError: onStompErrorCallback,
};
stompClient = new Client(stompConfig);
}
return stompClient;
}
export default useStompClient;
The solution? Create a 2nd Layer authentication:
- App
- AuthenticationLayer
- WebSocketLayer
Ta Da! Either login and open the app scenario, it would work as expected.
question from:https://stackoverflow.com/questions/66050308/how-to-use-dispatch-inside-onstomperrorcallback-of-stomp-stomp-js-library