What is right coding and using useState into Stomp js function? (Websocket) - javascript

I have a problem with Stomp js. It only renders once. I think the coding is not right. How can you suggest solutions? For example, I have active state. But it only renders the initial state. I want to make a Chat app and there are rooms (chat members). When changing room, activechanges, but when I send messages active does not change into stompclient
My codes
const [active, setActive] = useState(null);
const connect = () => {
let Sock = new SockJS(socketEndpoint);
stompClient = over(Sock);
stompClient.connect({}, onConnected, onError);
};
console.log(active) // AFTER RENDERING AND SETTING ANOTHER ELEMENT IT CHANGES
const onConnected = () => {
stompClient.subscribe(
"/user/topic/chat/message-result",
function (order_progress) {
console.log(active) // AFTER RENDERING AND SETTING ANOTHER ELEMENT IT DOES NOT CHANGE
let statusData = JSON.parse(order_progress.body);
if (statusData.status === chatMessageStatus.SUCCESS) {
if (active === statusData.roomId) {
...
useEffect(() => {
connect();
return () => disconnect();
}, [active]);

Related

Do i have to delete the event listener manually in websockets?

Does the following code create duplicate on.message listeners ?
when i execute on.message once, the payloads array takes it's initial value each time
export default function ProjectChat() {
const { id: projectId } = useParams()
const [payloads, setPayloads] = useState([])
const [messageBody, setMessageBody] = useState('')
const [webSocket, setWebSocket] = useState(null)
// Create the connection
useEffect(() => {
const url = `ws://localhost:3000/ws/${projectId}`;
const ws = new WebSocket(url);
setWebSocket(ws)
return () => ws.close()
}, [])
useEffect(() => {
if (webSocket !== null) {
webSocket.onmessage = e => {
const payload = JSON.parse(e.data);
const allMessages = [...payloads, payload]
setPayloads(allMessages)
}
}
}, [webSocket, payloads])
return <p>jsx</p>
}
You can't create duplicate listeners with on.message. Every time you define on.message, you're over-writing the previous listener.
The addListener is the method which can lead to multiple listeners. In that case, you would want to run removeEventListener() on the listener you created inside your component's unmount phase return () => removeEvenListener(...).

Why isn't onSnapshot listener working when exported to another file

I had an onSnapshot listener function set up, but once I extracted it to another file it stopped working.
The code below is in the utilities file.
export const getUserCampsites = () => {
const user = store.getState().authReducer.user
let campsitesArr = [];
//check if user is signed in
if(user.hasOwnProperty('uid')){
const campsites = db
.collection('campsites')
.where('owner', '==', user.uid);
const unsub = campsites
.onSnapshot(snapshot => {
snapshot.forEach(el => {
campsitesArr.push(el.data());
})
});
return {unsub, campsitesArr}
}
}
And this is what I have in the component:
const [camps, setCamps] = useState();
useEffect(() => {
const res = getUserCampsites()
if(res) {
const campsites = res.campsitesArr
setCamps(campsites);
return(() => res.unsub())
}
}, [user])
When 'setCamps' in the component is called the array comes back empty, but is then filled on a second re-render, am I missing something? Is it supposed to be asynchronous?
Thanks in advance!
The array campsitesArr in the getUserCampsites function is not reactive, so it is not re returned if its value changes.
When the getUserCampsites function is called, it inializes the campsitesArr to an empty arr and then it initializes the listener.. However it does not wait for the listener to fetch values before it moves on to the next line which is the return statement.
So whenever the return is reached, the campsitesArr is still enter, therefore you always return an empty array.
The best solution will be to pass a function as an argument to the getUserCampsites, that receives an array of camp sites. This function will be called whenever the listener gets new values.
For example, you can pass a function to the getUserCampsites that calls setState with the new array of camp sites
Not sure what was wrong, I think it was because the function was returning before the campsitesArr had been populated.
To fix this I passed the setCamps function as a prop and used it directly inside the onSnapshot call back:
export const getUserCampsites = (setCamps) => {
const user = store.getState().authReducer.user
//check if user is signed in
if(user.hasOwnProperty('uid')){
const campsites = db
.collection('campsites')
.where('owner', '==', user.uid);
const unsub = campsites
.onSnapshot(snapshot => {
let campsitesArr = [];
snapshot.forEach(el => {
campsitesArr.push(el.data());
})
setCamps(campsitesArr)
});
return unsub
}
}

stale state inside socket callback

I was trying to make socket connection using React hooks. I am getting a stale value when trying to access a state variable inside socket callback function. users variable inside socket callback is not having the latest value. Using useRef works but it does not rerender the component. What could be the better solution for this ?. Thanks!
const [users, setUsers] = useState([]);
const [socket, setSocket] = useState(null);
const result = useParams();
useEffect(() => {
const socket = io(`${config.host}?meetingId=${result.id}`);
socket.request = promise(socket);
setSocket(socket);
}, []);
useEffect(() => {
if (!socket) return;
let webrtc = null;
socket.on('UserLeft', ({ id }) => {
setUsers(users => users.filter(user => user.id !== id))
});
socket.on('UserAdded', ({ name, id }) => {
const newUser = new OtherUser(name, id);
setUsers(users => [...users, newUser])
})
socket.on('newProducer', async (data) => {
const { socketId, kind } = data;
const consumer = await webrtc.createConsumer(socketId, kind);
// Issue is here, the users array still has stale values
const user = users.find(ele => ele.id === socketId);
// Some more stuff with user
})
}, [socket]);
// Here the output is correct
console.log(users)
return <div>HELLO</div>
You could use useReducer to store mutating variables and add logic into the handler

How to add new objects to an existing useState array using map() or foreach()?

I'm trying to create a Twitter clone, and I'm having troubles with my news feed. Basically it pulls tweets from my firebase database for each user followed by the current user. So say you follow Jon Abrahams and Terry Crews, for each of these 2, it'll pull the "tweets" collection, and for each tweet, it'll return the data.
I did this with useState and useContext ( since I needed Context and couldn't make it work in a class component, but also needed state ).
const CurrentUser = useContext(CurrentUserContext);
const [tweets, setTweets] = useState({tweets: []});
const feedTheFeed = async (id) => {
const followedUsers = await getFollowedUsers(id);
if(followedUsers) {
followedUsers.docs.forEach(async doc => {
const followedId = doc.data();
const unformatTweets = await getTweetsByUser(followedId.follows);
if(unformatTweets.docs) {
unformatTweets.docs.map(unformatTweet => {
const tweetText = unformatTweet.data();
setTweets({
tweets: [...tweets, tweetText]
})
// console.log(tweetText);
})
}
})
// console.log(tweets);
}
}
useEffect(() => {
if(!CurrentUser) return;
if(CurrentUser.id || CurrentUser.uid) {
feedTheFeed(CurrentUser.id);
}
}, [CurrentUser]);
The problem is that there's an issue when loading the component, it says that "tweets is not iterable", but it's an array, so I don't see why it wouldn't work. Does anyone have an idea ?
Thank you !
Seems like what you want is
const [tweets, setTweets] = useState([]);
and
setTweets([...tweets, tweetText])
I think what you want to do is this.....
const [tweets, setTweets] = useState([]);
setTweets(b => [...b, tweetText])

Firebase data only loading after logging in and out

I am having an issue with loading data with firebase and react native. When I initially go to the screen no data is shown. Although, if I go back to the login screen and then log back in the data I was looking for is shown.
The code is as follows :
For the useeffect hook:
const ChatRooms = ({navigation}) => {
const [data, setData] = React.useState([]);
const [selectTitle, setTitle] = React.useState(new Map());
useEffect(() => {
const chatTitles = [];
const chats = firebaseSDK.loadChatRooms();
for(let i=0; i <chats.length; i++){
chatTitles.push({title: chats[i]});
}
setData(chatTitles);
}, []);
For the loadChatRooms :
loadChatRooms(){
const chatrooms = firebase.database().ref();
let check = [];
chatrooms.orderByKey()
.on("value", (data) => {
check = Object.keys(data.val());
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
return check;
};
I am struggling to find the issue. I am assuming it has something to do with firebase/useeffect interaction.
Thank you! :)
The problem is that you fail to handle the fact that data is loaded asynchronously.
The easiest way to show how to fix this is by putting the code that loads the data into yoru ChatRooms:
const ChatRooms = ({navigation}) => {
const [data, setData] = React.useState([]);
const [selectTitle, setTitle] = React.useState(new Map());
useEffect(() => {
const ref = firebase.database().ref();
ref.orderByKey().on("value", (data) => {
const chatTitles = [];
chats = Object.keys(data.val());
for(let i=0; i <chats.length; i++){
chatTitles.push({title: chats[i]});
}
setData(chatTitles);
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
}, []);
By moving the code that processes the data into the on() callback, it gets executed once the data has loaded. A fun side-effect of this, is that it also will update the state whenever the data changes in the database.
You can give this a try by changing one of the titles in your database, and watching the UI update in near realtime. And this will of course also work once you have multiple users, all writing messages to the database. 🎉
A slight improvement is to iterate over the results with the built-in forEach operator, which ensures the order will be consistent on all platforms (and also when you order on other properties):
const ref = firebase.database().ref();
ref.orderByKey().on("value", (data) => {
const chatTitles = [];
data.forEach((child) => {
chatTitles.push({title: child.val()});
});
setData(chatTitles);
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});

Categories