I am using vuejs (CLI 3) with axios and sockets. My backend is NodeJs.
Html (view all users):
...
<ul v-if="users.length > 0">
<li v-for="user in users" :key="user.id">
<router-link :to="'/oneuser/' + user.permalink" tag="li" active-class="active" #click.native="setmyid(user._id)">
<a>{{ user.name }} - {{ user.last_name }}</a>
</router-link>
</li>
</ul>
...
<script>
import axios from 'axios'
import io from 'socket.io-client'
export default {
name: 'get-user',
data () {
return {
users: [],
socket: io('localhost:7000')
}
},
methods: {
mycall () {
axios.get('http://localhost:7000/api/users')
.then(res => {
// console.log(res)
const data = res.data
const users = []
for (let key in data) {
const user = data[key]
user.id = key
users.push(user)
}
// console.log(users)
this.users = users
})
.catch(error => console.log(error))
}
}
mounted () {
this.mycall()
this.socket.on('user-deleted', function (data) {
this.mycall()
})
}
}
</script>
Html (user view):
<script>
import axios from 'axios'
export default {
name: 'one-user',
data () {
return {
name: '',
surname: '',
id: localStorage.getItem('usId'),
socket: io('localhost:7000')
}
},
mounted () {
axios.get('http://localhost:7000/api/get-user/' + this.id)
.then(res => {
const data = res.data
this.name = data.name
this.surname = data.last_name
})
.catch(error => console.log(error))
},
methods: {
mySubmit () {
const formData = {
_id: this.id
}
axios.post('http://localhost:7000/api/delete-user', formData)
.then(this.$router.push({ name: 'get-user' }))
.catch(error => console.log(error))
}
}
}
</script>
backend NodeJs:
controller.postDeleteUser = function(req,res){
User.deleteOne({"_id" : req.body._id}, function(err){
io.emit('user-deleted', req.body._id);
res.send('ok');
});
};
When I go to user view and delete the user then it directs me to view all users. I have two major problems here.
1) After redirect, I saw again all the users even the deleted one. In my database the user has deleted correctly.
2) I don't know where exactly and how to use sockets in my code.
I am using in the front the socket.io-client npm plugin. Also I don't want to use (and I don't use it in my code) vue-socket.io because IE 11 and below version are not supported and it throws me some errors.
What I have tried so far:
1) Using watch like this:
watch: {
users: function (newValue) {
newValue = this.mycall()
}
}
This is very bad for browser performance, because always call request from the browser.
2) use beforeUpdate or Updated life-cycle:
updated () {
this.mycall()
}
That works but has bad performance. It makes requests many times to the server.
3) or that with sockets
updated () {
this.socket.on('user-deleted', function (data) {
this.mycall()
})
}
and that throws me an error:
this.mycall() is not a function
What am I doing wrong?
Where to put the code with sockets?
I have changed the view all users file to:
...
methods: {
mycall () {
axios.get('http://localhost:7000/api/users')
.then(res => {
const data = res.data
const users = []
for (let key in data) {
const user = data[key]
user.id = key
users.push(user)
}
this.users = users
})
.catch(error => console.log(error))
},
socketcall (thecall) {
this.socket.on(thecall, (data) => {
this.mycall()
})
}
}
....
created () {
this.mycall()
},
mounted () {
this.socketcall('user-deleted')
}
Life cycle-hooks cannot retrieved functions inside "this.socket.on" so I thought to do like above and it works!
Related
EDIT: Simplified the code a bit by using two separate states. Adding the channel state to the dependency array of useEffect() does not remove the error but does make the application run in a strange manner: If I try to send messages, I see multiple messages.
EDIT 2: If I use the useRef() hook to store the state, my application works albeit in a strange manner again: the state is one step slower than the actual user input.
I'm trying to build a simple chat application using React.js and Socket.IO where a single user can communicate with multiple users. I have an endpoint from where I fetch all my channel data and store it in a state but I'm not able to access the state (returns null) even if the state is being passed down to a different component as props and that is being rendered correctly the first time. When I click on a channel to select it, the channel list is again reset to null and I get this error:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'forEach')
I'm trying to implement this by following the tutorial from here. It's outdated, so I'm implementing a newer version of it. But even then, if I use the component class defined in the author's github, it seems to work just fine. I have not worked with component classes before, so I am not sure where I am going wrong.
Main component: Chat.js
import React, { useEffect, useState } from 'react';
import ChannelList from './ChannelList';
import './chat.css';
import MessagePanel from './MessagePanel';
import { io } from "socket.io-client";
const SERVER = "http://localhost:8080";
let socket;
function Chat() {
const [channel, setChannel] = useState(null)
const [channels, setChannels] = useState(null) //the state where the channels are loaded
const channelsRef = useRef() //edit 2
useEffect(() => {
loadChannels() //the function which loads the channels into the state
configureSocket()
}, []) //adding channel to the dependency array results in strange behavior.
const configureSocket = () => {
socket = io(SERVER)
socket.on('connection', () => {
if(channel) {
handleChannelSelect(channel.id)
}
});
socket.on('channel', channel => {
console.log('channel details', channel); //returns the correct channel clicked on
//let temp_channels = channels; //edit 2
let temp_channels = channelsRef.current; //edit 2
console.log('channels inside onchannel', temp_channels); //returns null, does not return null if useRef() is used
temp_channels.forEach(c => { //I get the above mentioned error at this line, after edit 2 I don't get any error
if(c.id === channel.id) {
c.participants = channel.participants
}
})
//setChannels(channels); edit 2
channelsRef.current = temp_channels // edit 2
})
socket.on('message', message => {
// let temp_channels = channels
let temp_channels = channelsRef.current //edit 2
temp_channels.forEach(c => {
if (c.id === message.channel_id) {
if (!c.messages) {
c.messages = [message];
} else {
c.messages.push(message);
}
}
})
// setChannels(channels) edit 2
channelsRef.current = temp_channels // edit 2
})
}
const handleSendMessage = (channel_id, text) => {
socket.emit('send-message', { channel_id, text, senderName: socket.id, id: Date.now()})
}
const handleChannelSelect = (id) => {
const channel = channels.find(c => {
return c.id === id;
});
setChannel(channel);
socket.emit('channel-join', id, ack => {});
}
const loadChannels = async () => {
await fetch('http://localhost:8080/getChannels')
.then(async response => {
const data = await response.json()
console.log('data inside loadchannels', data.channels);
setChannels(data.channels); //this sets the channels and passes it down to the child component "ChannelList"
channelsRef.current = data.channels //if setChannels is not used before this, it doesn't work
})
console.log('after fetching channels inside loadchannels', channel, channels); //both channel and channels state are null
}
return (
<div className="chat-app">
<ChannelList channels={channels} onSelectChannel={handleChannelSelect}></ChannelList>
<MessagePanel onSendMessage={handleSendMessage} channel={channel}/>
</div>
);
}
export default Chat;
ChannelList.js
import React from 'react';
import Channel from './Channel';
function ChannelList(props) {
const handleClick = (id) => {
props.onSelectChannel(id);
}
console.log('inside channellist component', props.channels); //returns the set of channels received from props
let list = <div className="no-content-message">There are no channels to show</div>;
if (props.channels && props.channels.map) {
list = props.channels.map(c =>
<Channel
key={c.id}
id={c.id}
name={c.name}
participants={c.participants}
onClick={handleClick}
/>
);
}
return (
<div className='channel-list'>
{list}
</div>
);
}
export default ChannelList;
Backend index.js:
const express = require('express');
const app = express();
http = require('http');
const cors = require('cors');
const { Server } = require('socket.io');
app.use(cors());
const server = http.createServer(app);
const STATIC_CHANNELS = [
{
name: 'Global chat',
participants: 0,
id: 1,
sockets: []
},
{
name: 'Funny',
participants: 0,
id: 2,
sockets: []
},
{
name: 'Test',
participants: 0,
id: 3,
sockets: []
},
];
const io = new Server(server, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST'],
},
});
server.listen(8080, () =>
console.log('Server is running on port 8080')
);
io.on('connection', (socket) => {
console.log(`User connected ${socket.id}`);
socket.emit('connected', socket.id);
socket.on('channel-join', id => {
console.log('channel join', id);
STATIC_CHANNELS.forEach(c => {
if (c.id === id) {
if (c.sockets.indexOf(socket.id) == (-1)) {
c.sockets.push(socket.id);
c.participants++;
io.emit('channel', c);
}
} else {
let index = c.sockets.indexOf(socket.id);
if (index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
}
});
return id;
})
socket.on('send-message', message => {
io.emit('message', message);
})
socket.on('disconnect', () => {
STATIC_CHANNELS.forEach(c => {
let index = c.sockets.indexOf(socket.id);
if(index != (-1)) {
c.sockets.splice(index, 1);
c.participants--;
io.emit('channel', c);
}
})
})
});
app.get('/getChannels', (req, res) => {
res.json({
channels: STATIC_CHANNELS
})
})
i am currenty using the Clarifai API to detect faces, i also created that whenever i detect a face the numbers are moving up
see below imageurl image
however, when i click on detect, even without entering any url, the number still moves up, how can i prevent it from moving up when nothing is entered,
see my code below
FRONTEND code App.js
import React, { Component } from 'react';
import Particles from 'react-particles-js';
import FaceRecognition from './components/FaceRecognition/FaceRecognition';
import Navigation from './components/Navigation/Navigation';
import Signin from './components/Signin/Signin';
import Register from './components/Register/Register';
import Logo from './components/Logo/Logo';
import ImageLinkForm from './components/ImageLinkForm/ImageLinkForm';
import Rank from './components/Rank/Rank';
import './App.css';
const particlesOptions = {
particles: {
number: {
value: 30,
density: {
enable: true,
value_area: 800
}
}
}
}
const initialState = {
input: '',
imageUrl: '',
box: {},
route: 'signin',
isSignedIn: false,
user: {
id: '',
name: '',
email: '',
entries: 0,
joined: ''
}
}
class App extends Component {
constructor() {
super();
this.state = initialState;
}
loadUser = (data) => {
this.setState({user: {
id: data.id,
name: data.name,
email: data.email,
entries: data.entries,
joined: data.joined
}})
}
calculateFaceLocation = (data) => {
const clarifaiFace = data.outputs[0].data.regions[0].region_info.bounding_box;
const image = document.getElementById('inputimage');
const width = Number(image.width);
const height = Number(image.height);
return {
leftCol: clarifaiFace.left_col * width,
topRow: clarifaiFace.top_row * height,
rightCol: width - (clarifaiFace.right_col * width),
bottomRow: height - (clarifaiFace.bottom_row * height)
}
}
displayFaceBox = (box) => {
this.setState({box: box});
}
onInputChange = (event) => {
this.setState({input: event.target.value});
}
onButtonSubmit = () => {
this.setState({imageUrl: this.state.input});
fetch('https://ancient-sea-46547.herokuapp.com/imageurl', {
method: 'post',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
input: this.state.input
})
})
.then(response => response.json())
.then(response => {
if (response) {
fetch('https://ancient-sea-46547.herokuapp.com/image', {
method: 'put',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
id: this.state.user.id
})
})
.then(response => response.json())
.then(count => {
this.setState(Object.assign(this.state.user, { entries: count}))
})
.catch(console.log)
}
this.displayFaceBox(this.calculateFaceLocation(response))
})
.catch(err => console.log(err));
}
onRouteChange = (route) => {
if (route === 'signout') {
this.setState(initialState)
} else if (route === 'home') {
this.setState({isSignedIn: true})
}
this.setState({route: route});
}
render() {
const { isSignedIn, imageUrl, route, box } = this.state;
return (
<div className="App">
<Particles className='particles'
params={particlesOptions}
/>
<Navigation isSignedIn={isSignedIn} onRouteChange={this.onRouteChange} />
{ route === 'home'
? <div>
<Logo />
<Rank
name={this.state.user.name}
entries={this.state.user.entries}
/>
<ImageLinkForm
onInputChange={this.onInputChange}
onButtonSubmit={this.onButtonSubmit}
/>
<FaceRecognition box={box} imageUrl={imageUrl} />
</div>
: (
route === 'signin'
? <Signin loadUser={this.loadUser} onRouteChange={this.onRouteChange}/>
: <Register loadUser={this.loadUser} onRouteChange={this.onRouteChange}/>
)
}
</div>
);
}
}
export default App;
BACKEND code image.js
const Clarifai = require('clarifai');
const app = new Clarifai.App({
apiKey: '378c71a79572483d9d96c7c88cb13a7a'
});
const handleApiCall = (req, res) => {
app.models
.predict(Clarifai.FACE_DETECT_MODEL, req.body.input)
.then(data => {
res.json(data);
})
.catch(err => res.status(400).json('unable to work with API'))
}
const handleImage = (req, res, db) => {
const { id } = req.body;
db('users').where('id', '=', id)
.increment('entries', 1)
.returning('entries')
.then(entries => {
res.json(entries[0].entries)
})
.catch(err => res.status(400).json('unable to get entries'))
}
module.exports = {
handleImage,
handleApiCall
}
anything i can add to prevent it?
It seems the issue is with how you handle state object, and not with the Clarifai API. For example, instead of directly modifying the state using this.state, try using this.setState(). This way, when a value in the state object changes, the component will re-render, implying that the output will change according to the new values of the detected faces.
i figured the issue,
My frontend logic woudlnt work because I'm just returning a JSON object and not assigning any status code as such. So when i receive a JSON response you cannot say if(response) because in both the cases of success and failure you get a JSON and your if condition will always be true. So instead, i just wrapped the fetch call
with an if condition saying if(this.state.input) so that you handle the case where users cannot click on a button without entering an URL
Problem:
I am working on a chat application. When I send more than 9-10 requests, the browser slows down and eventually, it just hangs. On refreshing the page, everything is back to normal.
I searched the socket.io documentation but couldn't get any solution regarding this matter.
Code:
Here is my Backend Express.JS code:
index.js
const express = require("express");
const { addUser, getUser, deleteUser, getAllUsers } = require("./users_api");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
},
});
const port = 5000 || process.env.PORT;
io.on("connection", (socket) => {
socket.on("join", ({ name, room }, callback) => {
const { err, user } = addUser({ id: socket.id, name, room });
if (err) return callback(err);
if (user) {
socket.emit("message", {
user: "admin",
text: `${user.name} has entered the room ${user.room}.`,
});
socket.broadcast.to(user.room).emit("message", {
user: "admin",
text: `${user.name} has joined the room.`,
});
socket.join(user.room);
io.to(user.room).emit("users", {
room: user.room,
users: getAllUsers(user.room),
});
callback();
}
});
socket.on("client_message", (msg) => {
const user = getUser(socket.id);
if (user) io.to(user.room).emit("message", { user: user.name, text: msg });
});
socket.on("disconnect", () => {
console.log(6);
const user = deleteUser(socket.id);
if (user) {
io.to(user.room).emit("message", {
user: "admin",
text: `${user.name} has left the room.`,
});
io.to(user.room).emit("users", {
room: user.room,
users: getAllUsers(user.room),
});
}
});
});
server.listen(port, () => console.log(`Server started at port ${port}.`));
users_api.js:
const current_users = [];
const addUser = ({ id, name, room }) => {
name = name.trim().toLowerCase().split(" ").join("_");
room = room.trim().toLowerCase();
const exist_user = current_users.find(
(user) => name === user.name && room === user.room
);
if (exist_user)
return {
err: "User with this name already exists.",
};
current_users.push({ id, name, room });
return { user: { id, name, room } };
};
const deleteUser = (id) => {
const index = current_users.findIndex((user) => user.id === id);
if (index !== -1) return current_users.splice(index, 1)[0];
};
const getUser = (id) => current_users.find((user) => user.id === id);
const getAllUsers = (room) =>
current_users.filter((user) => user.room === room);
module.exports = { addUser, deleteUser, getUser, getAllUsers };
Front-end Code:
import io from "socket.io-client";
import React, { useEffect, useRef, useState } from "react";
import queryString from "query-string";
import "./ChatPage.css";
const END_POINT = "http://localhost:5000";
const ChatPage = (props) => {
const [message, setMessage] = useState("");
const [messages, setMessages] = useState([]);
const [users, setUsers] = useState([]);
const socket = useRef(null);
const handleSubmit = () => {
socket.current.emit("client_message", message);
setMessage("");
};
useEffect(() => {
const { name, room } = queryString.parse(props.location.search);
socket.current = io(END_POINT);
socket.current.emit("join", { name, room }, (error) => {
if (error) alert(error);
});
return () => {
socket.current.disconnect();
socket.current.off();
};
}, [props.location.search, END_POINT]);
useEffect(() => {
socket.current.on("message", (msg) => {
setMessages([...messages, msg]);
});
socket.current.on("users", ({ users }) => {
setUsers(users);
});
}, [messages.length]);
return (
<div className="chat-room-container">
<div className="chat-room-left">
<ul>
{messages.map((message, i) => (
<li key={i}>
{message.name}
{message.text}
</li>
))}
</ul>
<input
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<button type="button" onClick={handleSubmit}>
Submit
</button>
</div>
<div className="chat-room-right">
<ul>
{users.map((user, i) => (
<li key={i}>{user.name}</li>
))}
</ul>
</div>
</div>
);
};
export default ChatPage;
I don't understand what's wrong, please help.
Instead of sending a message on event or broadcast try to send list of messages to particular user or all users list with their messages belonging to same room. This will leads to less render in frontend.
For example,
Your Frontend code will change like,
useEffect(() => {
socket.current.on("message", (msgList) => {
setMessages(msgList);
});
socket.current.on("users", ({ users }) => {
setUsers(users);
});
}, [messages.length]);
Also if different rooms then there occurs a problem to filter the messages. So to solve this there are two possible ways:
When you send filter data from server.
Filter data in client according to user name and room name.
As this solution is not a good practice as every time the whole list of message will come and applying filter on that will increase time complexity. But you can reduce to some extent by making proper structure of data state on server.
I think this will help you.
You are creating new socket connection on every useEffect, so after ten messages, you have ten connections.
socket = io(END_POINT);
I store the created socket connection in useRef - so if it is created again, it is overwritten with the new one - and it does not duplicate.
const socketConection = useRef(null)
useEffect(() => {
socketConnection.current = io(END_POINT)
}, [deps])
Of course, in all following uses you have to use socketConection.current
this is my react js code and I want to connect with my node js API but I don't understand how to that ...!
import React, { useState } from "react";
import Poll from "react-polls";
// import "./styles.css";
/**
* https://stackoverflow.com/questions/65896319/react-js-class-poll-convert-into-react-hooks-poll
*/
// Declaring poll question and answers
const pollQuestion = "Youtube is the best place to learn ?";
const answers = [
{ option: "Yes", votes: 7 },
{ option: "No", votes: 2 },
{ option: "don't know", votes: 1 },
];
const Fakepolls = () => {
// Setting answers to state to reload the component with each vote
const [pollAnswers, setPollAnswers] = useState([...answers]);
// Handling user vote
// Increments the votes count of answer when the user votes
const handleVote = (voteAnswer) => {
setPollAnswers((pollAnswers) =>
pollAnswers.map((answer) =>
answer.option === voteAnswer
? {
...answer,
votes: answer.votes + 1,
}
: answer
)
);
};
return (
<div>
<Poll
noStorage
question={pollQuestion}
answers={pollAnswers}
onVote={handleVote}
/>
</div>
);
};
export default function App() {
return (
<div className="App">
<Fakepolls />
</div>
);
}
It work's fine with
// Declaring poll question and answers
const pollQuestion = "Youtube is the best place to learn ?";
const answers = [
{ option: "Yes", votes: 7 },
{ option: "No", votes: 2 },
{ option: "don't know", votes: 1 },
];
but I want to connect this poll with my API instead of Declaring it ..! this is my api- to get data -> ( router.get("/poll/:pollId", getPoll); //)
exports.getPoll = async (req, res, next) => {
try {
const { pollId } = req.params;
const polls = await Poll.findById(pollId);
if (!polls) throw new Error("no polls found");
res.status(200).json(polls);
} catch (error) {
error.status = 400;
next(error);
}
};
This is a postman image -
and this API for POST data- and my node js code -
exports.votes = async (req, res, next) => {
try {
/**
* 1. get the poll from db
* 2. check if the user already exists in any option
* 3. if user has already selected any option do nothing
* 4. if user has selected any other option remove from that option
* 5. if user does not exist in any option, insert his user id to selected option
*/
const { pollId } = req.params;
let { userId, answer } = req.body;
// get selected poll from db
const poll = await Poll.findById(pollId);
if (answer && poll) {
answer = answer.toLowerCase();
///Finf the Poll
let existingVote = null;
Object.keys(poll.options).forEach((option) => {
// loop on all options, check if the user already exists in any option
if (poll.options[option].includes(userId)) {
existingVote = option;
}
});
if (existingVote == null) {
// if there is no existing vote save it to db
try {
const push = {};
push[`options.${answer}`] = userId;
const update = await Poll.findByIdAndUpdate(
pollId,
{ $push: push },
{ upsert: true }
);
res.status(201).json(update);
} catch (err) {
error.status = 400;
next(error);
}
} else if (existingVote && existingVote.length > 0) {
// check if answer is same as previous, if yes send not modified
if (existingVote.toLowerCase() === answer.toLowerCase()) {
res.status(304).send("Response already saved");
} else {
// delete the previous response and save it in new
if (
Array.isArray(poll.options[existingVote]) &&
poll.options[existingVote].length > 0
) {
// TODO: filtering this is not returning array but 1
poll.options[existingVote] = poll.options[existingVote].filter(
(vote) => vote != userId
);
poll.options[answer] = poll.options[answer].push(userId);
const update = await Poll.findByIdAndUpdate(pollId, {
$set: { options: poll.options },
});
res.status(201).json(update);
}
}
} else {
error = {
status: 500,
message: "Something went wrong",
};
next(error);
}
} else {
error = {
status: 404,
message: "Poll not found",
};
next(error);
}
} catch (error) {
error.status = 400;
next(error);
}
};
this is a POSTMAN image using POST to store data --- >
how can I connect API with react poll
What you'd do is make a fetch() to your /api/polls endpoint inside your Fakepolls component, the URL being exactly as you show in your Postman screenshot. More info on fetch here at the MDN docs.
With the response you get from the endpoint, populate the answers array you component uses. From what I see, it would require a bit of transformation as your answer object is not quite the same as what Poll needs.
Next, upon user action, as well as updating the votes in the UI, you need to make another fetch to your vote endpoint.
Here's your component again with these adjustments. Keep in mind it's untested and the URLs are obviously not real:
import React, { useState, useEffect } from "react";
import Poll from "react-polls";
// import "./styles.css";
/**
* https://stackoverflow.com/questions/65896319/react-js-class-poll-convert-into-react-hooks-poll
*/
const Fakepolls = () => {
// Setting answers to state to reload the component with each vote
const [pollQuestion, setPollQuestion] = useState('');
const [pollAnswers, setPollAnswers] = useState([]);
// Query the actual poll info from the server
useEffect(() => {
fetch('http://your-server/api/polls/you-poll-id')
.then((response) => response.json()) //parse response as json
.then((pollObject) => {
let answerCountDictionary = Object.keys(pollObject.options)
.map(oKey => {
return {
option: oKey,
anwers: pollObject.options[oKey].length
}
}); //iterate over the 'options' properties' keys to get the names and the current votes
setPollAnswers(answerCountDictionary);
setPollQuestion(pollObject.question)
})
.catch((error) => {
console.error('Error:', error);
});
},[]) //adding empty array of dependencies to prevent multiple calls on state change
// Handling user vote
// Increments the votes count of answer when the user votes
const handleVote = (voteAnswer) => {
setPollAnswers((pollAnswers) =>
pollAnswers.map((answer) =>
answer.option === voteAnswer
? {
...answer,
votes: answer.votes + 1,
}
: answer
)
);
//also submit the backend
fetch('http://your-server/api/vote/poll-id', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
"userId": "the-logged-in-user",
"answer": voteAnswer
},
})
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
};
return (
<div>
<Poll
noStorage
question={pollQuestion}
answers={pollAnswers}
onVote={handleVote}
/>
</div>
);
};
export default function App() {
return (
<div className="App">
<Fakepolls />
</div>
);
}
So I have written a custom polling hook which uses useContext and useLazyQuery hooks. I want to write a unit test for this, which should cover its returned values state and side effect.
So far I have managed to do this much but I'm not so sure how to proceed ahead. Any tips?
export const useUploadActivityPolling = (
teId: TeIdType
): UploadActivityPollingResult => {
const { dispatch, uploadActivityId }: StoreContextType = useAppContext();
const [fetchActivityStatus, { error: UploadActivityError, data: UploadActivityData, stopPolling }] = useLazyQuery(
GET_UPLOAD_ACTIVITY,
{
pollInterval: 3000,
fetchPolicy: 'network-only',
variables: { teId, activityId: uploadActivityId },
}
);
useEffect(() => {
if (UploadActivityData) {
setUploadActivityId(
UploadActivityData.getUploadActivityStatus.activity_id,
dispatch
);
updateActivityStateAction(UploadActivityData.getExcelUploadActivityStatus.status, dispatch);
}
}, [UploadActivityData]);
return { fetchActivityStatus, stopPolling, UploadActivityError };
};
import React from 'react';
import { mount } from 'enzyme';
const TestCustomHook = ({ callback }) => {
callback();
return null;
};
export const testCustomHook = callback => {
mount(<TestCustomHook callback={callback} />);
};
describe('useUploadActivityPolling', () => {
let pollingResult;
const teId = 'some id';
beforeEach(() => {
testCustomHook(() => {
pollingResult = useUploadActivityPolling(teId);
});
});
test('should have an fetchActivityStatus function', () => {
expect(pollingResult.fetchActivityStatus).toBeInstanceOf(Function);
});
});