InvalidStateError while accessing WebSocket - javascript

I've configured a simple Vue project using the vue-cli tool:
vue init webpack my-project
Now I want to send some information through a web socket before the page gets rendered. Since I don't want to tie this logic to the Vue component, I have a different js file (named ws.js and based in this):
import Vue from 'vue'
const websocket = new WebSocket('ws://localhost:1234')
const emitter = new Vue({
name: 'emitter',
methods: {
send (message) {
websocket.send(JSON.stringify(message))
}
}
})
export default emitter
When the page loads, I use the emitter object to send some info:
<template>
<div class="hello">
TEST
</div>
</template>
<script>
import emitter from '../ws/ws'
export default {
name: 'HelloWorld',
beforeMount () {
emitter.send('Hello')
}
}
</script>
And I get this error in Firefox console:
[Vue warn]: Error in beforeMount hook: "InvalidStateError: An attempt
was made to use an object that is not, or is no longer, usable"
found in
---> at src/components/HelloWorld.vue
at src/App.vue
What am I doing wrong? Should I attach to a different event listener rather than beforeMount()? If I comment out the WebSocket related lines, the error disappears:
import Vue from 'vue'
// const websocket = new WebSocket('ws://localhost:1234')
const emitter = new Vue({
name: 'emitter',
methods: {
send (message) {
// websocket.send(message)
}
}
})
export default emitter

I need to write for the socket before it's in ready state before sending any message, so based in this answer I have changed ws.js to this:
import Vue from 'vue'
const websocket = new WebSocket('ws://localhost:1234')
// Make the function wait until the connection is made...
function waitForSocketConnection (socket, callback) {
setTimeout(
function () {
if (socket.readyState === 1) {
console.log('Connection is made')
if (callback != null) {
callback()
}
} else {
console.log('wait for connection...')
waitForSocketConnection(socket, callback)
}
}, 5) // wait 5 milisecond for the connection...
}
function sendWaiting (msg) {
waitForSocketConnection(websocket, () => {
console.log('Sending ' + msg)
websocket.send(msg)
console.log('Sent ' + msg)
})
}
const emitter = new Vue({
name: 'emitter',
methods: {
send (message) {
sendWaiting(message)
}
}
})
export default emitter
Now, before sending any message the application checks if the WebSocket is ready and sends it, otherwise it rechecks each 5 milliseconds until it is.

Related

How to access a component function from main.js in Vue 3 with Composition API

Using Vue 3 and composition API I have a component that have this function:
const retrieveSignedJWT = async (callback) => {
if (jwtUrl.value && !callback) {
//console.log("There's no callback use the by default URL")
await fetch(jwtUrl.value)
.then(async (response) => {
const data = await response.text();
// check for error response
if (!response.ok) {
// get error message from body or default to response statusText
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
let jwt = data;
token.value = data;
decodeToken(jwt);
retrieveCategories();
})
.catch((error) => {
errorMessage.value = error;
console.error("There was an error!", error);
});
} else {
//Function has a callback
token.value = callback;
}
};
What I need to do is to find a way to expose the previous component function so I can call it from the main.js. The scenario is that I'm creating an IIFFE with Vue 3 and Vite (a widget that the end user will load from a script) and hooking up a public function to it so the user can use it at any point in their code. That function can have or not have a callback that will expose a token implemented.
import { createApp } from "vue";
import "#/assets/styles/index.scss";
import App from "./App.vue";
import store from "./store";
let div = document.createElement("div");
document.body.appendChild(div);
div.setAttribute("id", "my-widget");
window.myWidget = {
load: function (endUserRetrievedJWT) {
if (endUserRetrievedJWT) {
const endUserJWT = endUserRetrievedJWT();
//Calling my component function w a call back
retrieveSignedJWT(endUserJWT);
} else {
//Calling my component function without a call back
retrieveSignedJWT();
}
},
};
createApp(App).use(store).mount("#my-widget");
So basically I'm trying to find a way to invoke the parent component function from the main.js file in order to manage how to save a token to the state of my application. That token can come from a default URL or in the shape of a callback function that the end-user will pass as an argument to the globally expose function coming from main.js.
main.js is meant for setting up your Vue application. Don't use this for any other code! It won't work.
Simply create a separate .js file (e.g. utils.js) and export the function from there.
I'm not completely sure what you're trying to achieve exactly but it looks like an authentication process for logging in. What you're probably trying to do is call the login/logout functions from within a Vue component? This could be as simple as
// Separate file, e.g. `authentication.js`
export const login = (cb) => {
// do not name the callback function (cb) 'callback' as that may get you unexpected behavior
// Your code
somePromise().then((res) => {
cb(res);
});
}
// Your component
import { login } from 'path/to/authentication';
login(myCallback);
const myCallback = (res) => {
...
};
I finally ended up exposing the function in the mounted hook like this:
onMounted(() => {
window.myWidget = {
load: retrieveEndUserJWT,
};
retrieveDataAttributes();
callWebsocket(websocket.value);
});
Then inside the same component, I create a method that will process the callback:
const retrieveEndUserJWT = async (endUserRetrievingTokenCallback) => {
const jwt = await endUserRetrievingTokenCallback();
processingToken(jwt);
};
And in the processingToken method, I deal with that token coming from the end-user callback function. I still have to navigate the pros and cons of exposing the function in the mounted hook.

How to use STOMP for connecting with ActiveMQ in React js

Can anyone provide successful implementation of Stomp using the latest version of ActiveMQ 5.x using the React N? How to connect and publish to the queue?
I have below questions:
I have to retrieve the data from consumer and do some add some boolean value and send it to publish.
How can I keep the connection alive because continuously I will get message in queues.
How can I implement this in React.js in simple manner (any plugins)
I tried with JavaScript, and it works as expected.
consumer.js
const Stomp = require("stomp-client");
const stompClient = new Stomp("127.0.0.1",61613);
stompClient.connect( function(sessionId){
console.log("consumer connected");
stompClient.subscribe("/queue/<name>",function(body){
console.log(body);
});
});
producer.js
const Stomp = require("stomp-client");
const stompClient = new Stomp("127.0.0.1",61613);
stompClient.connect( function(sessionId){
console.log("producer connected");
stompClient.publish("/queue/<name>",function(body){
console.log(body);
console.log(typeof(body));
//JSON.stringify(body);
});
stompClient.disconnect();
});
This is what I tried in React.JS (which failed): here i can able to connect and after that if i call subscribe with que name it is not giving any response
import './App.css';
import React,{useEffect} from 'react';
import { Client, Message } from '#stomp/stompjs';
function App() {
const clientdata = new Client();
useEffect(() => {
clientdata.configure({
brokerURL: 'ws://localhost:61614/stomp',
onConnect: (frame) => {
console.log('onConnect');
console.log(frame);
clientdata.subscribe('/queue/<quename>',info => {
console.log(info);
})
console.log(subscription);
},
// Helps during debugging, remove in production
debug: (str) => {
// console.log(new Date(), str);
}
});
client.activate();
}, []);
return (
<div >
</div>
);
}
export default App;
When i tried the above code I am getting only connected log and I'm not able to subscribe any thing and not seeing anything.

socket.io in Svelte via store

I'd like to have socket.io available across the whole Svelte app. I don't know what I am doing wrong...
store.js
export const socket = writable();
This works
App.svelte
import { io } from "socket.io-client";
import { socket } from "./stores.js";
$socket = io();
$socket.on("orders", (orders) => {
console.log(orders);
});
This doesn't
App.svelte
import { io } from "socket.io-client";
import { socket } from "./stores.js";
$socket = io();
Component.svelte
import { socket } from "./stores.js";
$socket.on("orders", (orders) => {
console.log(orders);
});
The code shown works as long as the execution order is guaranteed to be correct, i.e. the code in App running before the code in Component.
If it is not, you need to add additional logic to handle the store not being set. (You also probably should clean up these on event handlers via onDestroy as well.)
E.g. you can use a reactive statement to check whether the store was set, if it is initialized with null:
$: if ($socket != null) {
$socket.on("orders", (orders) => {
console.log(orders);
});
}

How testing my API calls in differents groups of test?

Im starting with react-testing-library, and Im trying to test API calls. I have two sets, one for success request and another for error request.
import React from "react";
import { render, waitForElementToBeRemoved } from "#testing-library/react";
import user from "#testing-library/user-event";
import App from "./App";
import { getUser } from "./serviceGithub";
jest.mock("./serviceGithub");
//Mock data for success and error, Im using the github api
const dataSuccess = {
id: "2231231",
name: "enzouu",
};
const dataError = {
message: "not found",
};
const renderInit = () => {
const utils = render(<App />);
const inputUser = utils.getByPlaceholderText("ingrese usuario", {
exact: false,
});
const buttonSearch = utils.getByRole("button", { name: /buscar/i });
return { utils, buttonSearch, inputUser };
};
test("should success request to api", async () => {
getUser.mockResolvedValue([dataSuccess]);
const { utils, buttonSearch, inputUser } = renderInit();
expect(utils.getByText(/esperando/i)).toBeInTheDocument();
expect(buttonSearch).toBeDisabled();
user.type(inputUser, "enzzoperez");
expect(buttonSearch).toBeEnabled();
user.click(buttonSearch);
await waitForElementToBeRemoved(() =>
utils.getByText("cargando", { exact: false })
);
expect(getUser).toHaveBeenCalledWith("enzzoperez");
expect(getUser).toHaveBeenCalledTimes(1);
expect(utils.getByText("enzouu", { exact: false })).toBeInTheDocument();
});
test("should error request to api", async () => {
getUser.mockResolvedValue(dataError)
const { utils, buttonSearch, inputUser } = renderInit();
expect(buttonSearch).toBeDisabled();
user.type(inputUser, "i4334jnrkni43");
expect(buttonSearch).toBeEnabled();
user.click(buttonSearch)
await waitForElementToBeRemoved(()=>utils.getByText(/cargando/i))
expect(getUser).toHaveBeenCalledWith('i4334jnrkni43')
expect(getUser).toHaveBeenCalledTimes(1)
});
The problem here is that in the second test the last line expect(getUser).toHaveBeenCalledTimes(1) get error because getUseris calling 2 times, but if I comment the first test, the second pass..
So, how should I do to test this case? Its ok the way that Im doing the tests?
Thanks!
You can use jest.mockClear() with beforeEach() or afterEach()
For clean-up purpose, afterEach() would be more appropriate.
mockClear resets all the information stored in the mockFn.mock.calls which means that for every test, you can expect getUser being called, started from zero times.
afterEach(() => {
jest.clearAllMocks()
})
Furthermore, use screen from #testing-library/react instead of returned value of render when using queries. Also, mockResolvedValueOnce would be better in this case.

Integrate Parse-SDK-JS to react antive app

I tried to google some guides or tutorials and didn't found any. Is there any way to integrate into my react-native app a Parse SDK same as to android and iOS way? Because I tried to integrate Parse-SDK-JS and it didn't work now my app, crashing with error Error: Unable to resolve module crypto from node_modules\parse\node_modules\crypto-js\core.js: crypto could not be found within the project.'
my code:
parse.ts:
// In a React Native application
import Parse, { Error } from 'parse';
// On React Native >= 0.50 and Parse >= 1.11.0, set the Async
import { AsyncStorage } from 'react-native';
export const initParse = () => {
Parse.setAsyncStorage(AsyncStorage);
Parse.initialize('xxxxxxxxxxxxxxxxxxxxxxxxxxx'); //APP_ID
Parse.serverURL = 'https://xxx.herokuapp.com/parse'; //HEROKU URI SERVER
};
export const testCreate = () => {
const GameScore = Parse.Object.extend('GameScore');
const gameScore = new GameScore();
gameScore.set('score', 1337);
gameScore.set('playerName', 'Sean Plott');
gameScore.set('cheatMode', false);
gameScore.save().then(
(gameScore: any) => {
// Execute any logic that should take place after the object is saved.
alert('New object created with objectId: ' + gameScore.id);
},
(error: Error) => {
// Execute any logic that should take place if the save fails.
// error is a Parse.Error with an error code and message.
alert('Failed to create new object, with error code: ' + error.message);
},
);
};
index.js:
/**
* #format
*/
import {
AppRegistry
} from 'react-native';
import {
initParse
} from './src/library/parse/parse';
import App from './App';
import {
name as appName
} from './app.json';
initParse();
AppRegistry.registerComponent(appName, () => App);
You need to import Parse from parse/react-native.js. It should be something like this:
import Parse, { Error } from 'parse/react-native.js';

Categories

Resources