I have an react app and I am using a service worker for offline purpose, I have a custom hook, this hook sends a request to check connection. If the request return a response of 200 it mean the user is online else offline. Currently I am getting a 200 ok from service worker, but the network is off. I would like to bypass the service worker and let the request run normally. The goal is to ensure the request does not return 200 while offline
here is my hook :
import { useEffect, useState } from 'react';
export const useOnline = () => {
const [online, setOnline] = useState(true);
const checkOnline = async () => {
try {
const response = await fetch('/hello.png');
setOnline(response.ok);
} catch {
setOnline(false);
}
};
useEffect(() => {
const change = () => {
setOnline(!online);
};
checkOnline();
window.addEventListener('online', () => change());
window.addEventListener('offline', () => change());
return () => {
window.removeEventListener('online', () => change());
window.removeEventListener('offline', () => change());
};
}, [online]);
return online;
};
I was thinking to add this in my service worker
self.addEventListener('fetch', function (event) {
if (event.request.url.match('^.*(/hello.png/).*$')) {
return false;
}
});
Not possible, because your hook will automaticly send 200 message.
It's a violation of service worker's goal, and your code will not work
Related
I have a react project setup with Redux and Axios. This is a function I am using to get data from an endpoint in my Redux actions:
export const getCSEfirstStageApplicants = () => async (dispatch) => {
try {
dispatch(LOADING());
const response = await axios.get(
`${baseUrl}/Franchisee/CSEFirstStageApplication`
);
if (response.status === 200) {
const { message, data } = response?.data || {};
return { message, data };
}
} catch (error) {
const { message } = error?.response?.data || {};
return message;
} finally {
dispatch(STOP_LOADING());
}
};
My component looks something like this:
import { useState, useEffect } from "react";
import {getCSEfirstStageApplicants} from "../../../redux/user/actions";
import { useDispatch } from "react-redux";
const MyComponent = () => {
const [cseApplicants, setCseApplicants] = useState([]);
const dispatch = useDispatch();
const getFirstStage = async () => {
const response = await dispatch(getCSEfirstStageApplicants());
if (response && response.data) {
console.log(response);
setCseApplicants(response.data);
return;
}
setCseApplicants([]);
};
useEffect(() => {
getFirstStage();
}, [dispatch]);
}
Apparently, this is working fine on my localhost. But when I build the app and push it to the server, it is giving an error on Chrome and Firefox and is working on Edge (browsers I have tested), indicating that response is undefined.
Chrome shows this error:
Firefox shows this error:
At first I thought it was the way the network call was made as preflight seemed to come after the xhr request. But checking Chrome showed that wasn't the error.
Another indication was an error that showed up as asyncgenerator error. I haven't been able to find a relation with this.
add properties to the empty object
const { message, data } = response?.data || {data:[], message:''};
I found something about this bug I explained at end;
Component codes
async fetch(){ await this.$store.dispatch('bots/getBots') },
computed: { ...mapState('bots', ['bots']) },
Store codes
export const state = () => {
return {
bots: []
}
}
export const mutations = {
UPDATE_BOTS(state, bots) {
state.bots = bots
}
}
export const actions = {
getBots({commit}) {
this.$axios.$get('url', {headers: {uid: '12345'}})
.then(res => {
commit('UPDATE_BOTS',res.robots)
})
.catch(e => {
console.log(e)
})
}
}
Issue: When moving between pages via nuxt-link data loads perfectly but when I reload the page bots state is empty...
Found Issue:
I use nuxt-auth and I had one plugin for checking status of axios request that if it was 401 unauthorized I logout user if he was loggedIn, So status undefined error was from here but I commented the plugin codes and I got other error from nuxt-auth that causes that problem I had So I related that issue in other question u can see it here:
Nuxt-Auth Bug: Looks for autherization in any get request that has headers config
It is the expected behavior. Vuex state is kept in memory and when you reload the page it gets purged.
Instead of this state
export const state = () => {
return {
bots: []
}
}
try this
export const state = () => ({
bots: []
})
I have an App.js file that contains a form that when on submitted, causes triggers a state change to render a new page. I'm trying to create a mock Jest test that does these steps:
Take mock data
Sends a POST request like addInfo is doing
Checks if "DONE WITH FORM" is rendered onto the screen.
I also had an idea that we could just fill out a form that takes in the valid_address and valid_number and click a button that triggers the addInfo function to run with the information passed in however I'm unsure of that method and it leads me to a CORS error.
From what I've seen on the web, I think mocking this addInfo using Jest and then testing what is rendered is the best way to go however I'm completely stuck on building this test.
Here's what I have for my App.js
const addInfo = async (formInfo) => {
try {
let data = {
valid_number: formInfo.validNumber,
valid_address: formInfo.validAddress
}
let addUserUrl = process.env.REACT_APP_URL +'/verify'
let addUserData = await fetch(
addUserUrl,
{
method: "POST",
headers: {
"Content-type": "application/json",
"x-api-key": process.env.REACT_APP_KEY
},
body: JSON.stringify(data)
}
)
if (addUserData.status !== 200) {
throw 'Error adding User'
}
let addUserDataJson = addUserData.json()
let ret = {
added: true,
}
return ret
} catch (error) {
console.log('Error')
let ret = {
added: false,
}
return ret
}
}
const onFinish = async (values: any) => {
console.log('Transaction verified');
let addStatus = await addInfo({
validNumber: "123434",
validAddress: "D74DS8JDSF",
})
if (promoStatus.added) {
setState({
...state,
showPage: false
})
} else {
setState({
...state,
showPage: true
})
}
};
return (
{!state.showPage &&
<>
<div>
<p>
DONE WITH FORM
</p>
<div>
</>
}
)
Here's what I've tried in App.test.js:
it('DONE WITH FORM APPEARS', async() =>{
// Render App
const { getByPlaceholderText, queryByText, getByText } = render(<App />);
// Entering Valid Number
const validNumberInputBox = getByText('Enter Valid Number);
fireEvent.change(validNumberInputBox, { target: { value: "123434" } });
expect(validNumberInputBox).toHaveValue("123434");
// Entering Valid Address
const validAddressInputBox = getByText('Enter Valid Address');
fireEvent.change(validAddressInputBox, { target: { value: "D74DS8JDSF" } });
expect(validAddressInputBox).toHaveValue("D74DS8JDSF");
// Button Click
userEvent.click(screen.getByRole('button', {name: /Submit/i}));
//Check if the DONE WITH FORM is shown
expect(await waitFor(() => getByText('DONE WITH FORM'))).toBeInTheDocument();
});
I've tried almost everything I could find through other stack overflow posts and web articles. so I'd really appreciate any help on how to implement this unit test.
The first step would be to mock the async function performing the POST request (addInfo). You never want to try real HTTP requests in unit tests (this won't work since Jest runs in a Node environment where fetch or XMLHttpRequest APIs are not implemented). Beside this, component/unit tests should be independent from any other system like a backend exposing APIs.
To do so, your async function should be in a separate file (so a JS module), then you could mock this module using Jest :
// api.js
export const addInfo = () => {...}
// App.test.js
import * as Api from 'api.js';
// here you can define what your mock will return for this test suite
const addInfoSpy = jest.spyOn(Api, 'addInfo').mockResolvedValue({ ret: true });
describe('...', () => {
test('...', async () => {
// perform user interactions that should trigger an API call
expect(addInfoSpy).toHaveBeenCalledWith('expected addInfos parameter');
// now you can test that your component displays "DONE WITH FORM" or whatever
// UI it should be displaying after a successful form submission
});
});
https://jestjs.io/docs/mock-function-api#mockfnmockresolvedvaluevalue
https://jestjs.io/docs/jest-object#jestspyonobject-methodname
https://jestjs.io/docs/expect#tohavebeencalledwitharg1-arg2-
I have a server backend written in Python with Flask-SocketIO. I'm utilizing it's room feature to make private conversations. Upon a join room event the server fires the following function to let the frontend know where to send messages to specific user:
socketio.emit('room name response', {'roomName': room_name, 'recipient': recipient}, to=sid)
where sid is the private room created only for the user when connecting to a socket. Then I want to keep this information in React state in a map, like this:
function ChatWindow({ username, token }) {
const [responses, setResponses] = useState([]);
const [roomsMap, setRoomsMap] = useState(new Map());
const [currentRoom, setCurrentRoom] = useState("");
const [messageValue, setMessageValue] = useState("");
var socket = null;
useEffect(() => {
socket = socketIOClient(ENDPOINT);
});
useEffect(() => {
socket.on("global response", (data) => {
setResponses((responses) => [...responses, data]);
});
socket.on("room name response", (data) => {
console.log(`joined ${data.roomName} with ${data.recipient}`);
setCurrentRoom((currentRoom) => data.roomName);
setRoomsMap((roomsMap) => roomsMap.set(data.recipient, data.roomName));
});
return () => socket.close();
}, []);
const sendMessage = () => {
if (messageValue.length < 1) {
return;
}
socket.emit("global message", {
user_name: username,
message: messageValue,
timestamp: Date.now(),
});
setMessageValue("");
};
const joinRoom = (recipient) => {
socket.emit("join", {
token: token,
username: username,
recipient: recipient,
});
// setCurrentRoom(() => roomsMap.get(recipient));
};
const leaveRoom = (recipient) => {
socket.emit("leave", {
token: token,
username: username,
recipient: recipient,
});
const newRooms = roomsMap;
newRooms.delete(recipient);
console.log(`left room with ${recipient}`);
newRooms.forEach((val, key) => console.log(`${val}:${key}`));
setRoomsMap(newRooms);
};
const checkUser = (userToCheck) => {
if (userToCheck === username) {
return styles.chatFromUser;
} else {
return styles.chatToUser;
}
};
return (...);
}
export default ChatWindow;
Sadly, React doesnt react to the socket emitting message, even though it can be seen in network tab in developer tools. The global response works fine.
When I alter the backend function to:
socketio.emit('room name response', {'roomName': room_name, 'recipient': recipient})
React suddenly works as expected. I'm trying to understand why it happens, especially when the browser seems to see the incoming messages as stated above, so it's most likely my bad coding or some React/Javascript thing.
Thank You for any help in advance.
The problem was that socket sometimes was created multiple times, therefore, the socket that useEffect was currently listening wasn't necessarily the one in the room. So I made one, global socket to fix this and whole thing now works.
For my sample application I was trying to check whether internet connection is there or not, I was using a reusable hook for this as shown below:
function useNetwork() {
const [isOnline, setNetwork] = useState(window.navigator.onLine);
const updateNetwork = () => {
setNetwork(window.navigator.onLine);
};
useEffect(() => {
window.addEventListener("offline", updateNetwork);
window.addEventListener("online", updateNetwork);
return () => {
window.removeEventListener("offline", updateNetwork);
window.removeEventListener("online", updateNetwork);
};
});
return isOnline;
}
The problem is it's working in Chrome, but when I checked in Safari when I tried turning off the wifi still isOnline returns true, I tried in the console of safari also, as window.navigator.isOnline it returns true.
Reading through different questions, here Danilo recommends to send a httpRequest and check it.
So should I send a get request to any site like google.com so I can know the status and set my onLine value as true. Because I think I need to poll this, since the above hook takes care when the dom is changed.
How can I achieve this, or is there any better way? How can I implement this in the above reusable hook.
you can do something like this,and have an api end point/or call any reliable api
const useNetworkStatus = () => {
const [isOnline, setStatus] = useState();
useEffect(() => {
(async () => {
try {
window.networkPoll = setInterval(async () => {
const networkStatus = await fetch(/*your api*/);
if (networkStatus.code != 200) {
setStatus(false);
}
if (networkStatus.code == 200) {
setStatus(true);
}
}, 5000);
}
catch (error) {
setStatus(false);
}
})();
}, []);
};