I am facing a problem that the broadcast channel is not listening sometime in useEffect. There is some list and upon clicking individual item, the broadcast postmessage should be emitting message to the listener of another file. Sometime it's working but for some reason, it's not receiving that event.
This snippet is located at index.js. Upon changes happened in dependencies, I am getting debug message all the time however it's not going inside onMessage.
useEffect(() => {
const messageBus = new BroadcastChannel('channelABC');
console.debug("ITS COMING HERE ALL THE TIME");
const onMessage = ({ data: message }) => {
if (message.type === 'ABCFired') {
showABC(message);
}
};
messageBus.addEventListener('message', onMessage);
return () => {
messageBus.removeEventListener('message', onMessage);
messageBus.close();
};
}, [dependencies]);
This is another file where message is being emitted.
const onRowClicked = index => {
setId(oldId => {
if (oldId !== index) {
console.debug("ITS ALSO WORKING");
messageBus.postMessage({ type: 'ABCFired', message });
return index;
}
else {
return null;
}
});
};
I am searching through the same answer right now. I have found that maybe when the broadcast channel shoots a message, the component gets unmount and the event listener is removed.
So by the time the message is fired, there is no listener.
Make a console statement in return function of use effect called
console.log('unmount)
You already have a console in onRowClicked, so check when you fire, onRowClicked, do you get unmount after that or something.
Quickfix
Remove the dependency from use Effect. In your case you want to enable eventListner once the app starts, I think.
Related
I want to pass the watch time of a video the user has seen when user closes the page,reload the page or navigate to another page. I am using visibilityChange event for this. When i try to navigate to another page, the api call runs perfectly. But the data i am sending to the api is not updated correctly. I am going to provide the code and the output below so you can understand perfectly what my problem is.
useEffect(async () => {
const x = 0;
console.log("use effect is run number ::::", x + 1);
window.addEventListener("visibilitychange", sendViewTime);
return () => {
window.removeEventListener("visibilitychange", sendViewTime);
};
}, []);
I have added the event listener in the useEffect.
the sendViewTime method is the method i want to call on visibility change event. This Method is working perfectly but for some reason the params are not updated even though i have set their states in their relavant hooks.
const sendViewTime = async () => {
if (document.visibilityState === "hidden") {
console.log("the document is hidden");
const value = localStorage.getItem("jwt");
const initialValue = JSON.parse(value);
console.log("the send View Time is :::", played_time);
const params = {
video_id: video_id_url,
viewTime: played_time,
MET: MET_value,
weight: "",
};
console.log("params are :::", params);
await setEffort(params, initialValue).then((res) => {
console.log("set effort api response is ::: ", res);
});
} else {
console.log("the document is back online");
}
};
//This onProgress prop is from react Player. Here i am updating the state of video progress.
onProgress={(time) => {
console.log("the time is :::", time);
const time_1 = Math.round(time.playedSeconds);
const time_2 = JSON.stringify(time_1);
setPlayed_time(time_2);
console.log("the played time is :::", played_time);
}}
//OUTPUT
// the document is hidden.
// the send View Time is :::
//params are ::: {video_id: '23', viewTime: '', MET: undefined, weight: ''}
//set effort api response is ::: {status: 200, message: 'Success', data: {…}, time: '2.743 s'}
//the document is back online
Never mind guys. I found the solution. It seems that i have to pass played_time and met value as a prop to the useEffect.If you want to know how useEffect works please visit this link. In general is it better to use one or many useEffect hooks in a single component?.
Currently, I have a video.js player with React. My goal is to fire off a function when the user starts playing the video and to fire a separate function when the user plays the video back again after pausing the video(let's call this the "continue" event). How can I achieve this without firing the incorrect function at start and continue?
I've looked at several different HTML media events such as 'play' and 'playing', however the "continue" function is still firing at start.
Here's what I have:
const VideoPlayer = ({ handleOnPlayContinue, handleOnStart }) => {
const [hasStarted, setHasStarted] = useState(false);
useEffect(() => {
videoJSPlayer.on('play'), () => {
setHasStarted(true);
};
videoJSPlayer.on('playing'), () => {
handleOnPlayContinue();
};
}, [videoJSPlayer, handleOnPlayContinue]);
useEffect(() => {
if (hasStarted) {
handleOnStart();
}
}, [hasStarted, handleOnStart]);
}
Should I be toggling another boolean state on 'pause' and use that boolean flag to fire my continue function? I thought of setting another state to manage when the user is continuing the video after pausing, but wanted to know if there's another HTML media event that I can leverage.
Thanks for any tips and answers you may have!
I don't believe there is a relevant HTML media event; you are going to have to manage it yourself with state and within just the play event. See if the below works for what you are trying to achieve (I have not been able to test it). Keep in mind that changes to state in useEffect aren't updated synchronously, and hasStarted needs to be in the dependency array otherwise the event will reference stale state variables. We also want to clean up event listeners between useEffect calls.
const VideoPlayer = ({ handleOnPlayContinue, handleOnStart }) => {
const [hasStarted, setHasStarted] = useState(false);
useEffect(() => {
videoJSPlayer.on("play", () => {
if (!hasStarted) {
handleOnStart();
setHasStarted(true);
} else {
handleOnPlayContinue();
}
});
return () => {
videoJSPlayer.off("play");
};
}, [hasStarted, handleOnPlayContinue, handleOnStart, videoJSPlayer]);
};
I've created some tests in Cypress to add and duplicate article in our Angular application. The code for test ArticlesTest.js
describe('Shop articles single station', () => {
const productManagementPage = new ProductManagementPage()
const shopArticlesPage = new ShopArticlesPage()
before(() => {
var credentials =
{
"username": "someusername#user.name",
"password": "strongPassword!"
}
cy.navigateToProductManagement(credentials)
})
beforeEach(() => {
productManagementPage.shopArticlesProgrammingButton().click()
shopArticlesPage.WaitUntilPageLoaded('/productmanagement/api/v1/articles/get', 'GetArticles')
})
it('Add article', () => {
var randomNumber = RandomDataGenerator.GenerateRandomInt(1000,4999)
var randomName = RandomDataGenerator.GenerateRandomString(20)
var randomPrice = RandomDataGenerator.GenerateRandomDecimal(1,99)
shopArticlesPage.newArticleButton().click()
shopArticlesPage.saveButton().should('be.disabled')
shopArticlesPage.undoButton().should('be.disabled')
shopArticlesPage.deleteButton().should('be.disabled')
shopArticlesPage.articlesList().should('not.exist')
shopArticlesPage.articleNumberTextBox().should('be.enabled')
shopArticlesPage.articleNumberTextBox().type(randomNumber)
shopArticlesPage.articleNameTextBox().type(randomName)
shopArticlesPage.articleUnitPriceTextBox().type(randomPrice)
shopArticlesPage.undoButton().should('be.enabled')
shopArticlesPage.saveButton().click()
shopArticlesPage.newArticleButton().should('exist')
shopArticlesPage.articlesList().should('exist')
shopArticlesPage.saveButton().should('be.disabled')
shopArticlesPage.undoButton().should('be.disabled')
})
it('Duplicate article', () => {
var articleNumber = RandomDataGenerator.GenerateRandomInt(51,65)
var newArticleNumber = RandomDataGenerator.GenerateRandomInt(1000, 4999)
var newArticleName = RandomDataGenerator.GenerateRandomString(20)
shopArticlesPage.articlesList().selectFromList(articleNumber)
const articleUnitPrice = shopArticlesPage.articleUnitPriceTextBox().invoke('text')
const vatCodeValue = shopArticlesPage.vatCodeDropDown().invoke('text')
const cardCodeValue = shopArticlesPage.cardCodeDropDown().invoke('text')
shopArticlesPage.duplicateArticleButton().click()
shopArticlesPage.WaitUntilPageLoaded()
shopArticlesPage.articleNumberTextBox().type(newArticleNumber)
shopArticlesPage.articleNameTextBox().type(newArticleName)
shopArticlesPage.saveButton().click()
shopArticlesPage.newArticleButton().should('be.enabled')
})
WaitUntilPageLoaded() method code is:
WaitUntilPageLoaded(path, alias) {
return cy.waitForRequestToComplete(path, alias)
}
which, in turn, is custom Cypress command:
Cypress.Commands.add('waitForRequestToComplete', (path, alias) => {
cy.intercept('POST', path).as(alias)
cy.wait('#' + alias).its('response.statusCode').should('be.ok')
})
In 1st beforeEach() run, there's no problem with intercepting GetArticles and waiting for it to complete.
The problem starts in 2nd test, as it looks like GetArticles is not intercepted, it's not called at all, though it's supposed to be. The problem doesn't exist when clicking through the application manually, and /articles/get is always invoked.
The test ends up with error message
Timed out retrying after 30000ms: cy.wait() timed out waiting 30000ms for the 1st request to the route: GetArticles. No request ever occurred.
I've also tried using other endpoint e.g. vatcodes/get, and it works perfectly. The problem occurs only for articles/get, but I don't see any trail that would tell my why this happens for articles endpoint.
What is the problem? Why Cypress "blocks" 2nd call to this endpoint? What's more interesting, the problem doesn't exist for GetFeatures alias, which is created in an identical way.
Make sure the network intercept is registered before the application makes the call.
it('is registered too late', () => {
cy.intercept('/todos').as('todos')
cy.visit('/')
cy.wait('#todos')
})
In our case, we need to register the intercept before visiting the page. Once the page is loaded, the application fetches the todo items, and everything is working as expected.
you can see this link: https://glebbahmutov.com/blog/cypress-intercept-problems/
If I'm reading the situation correctly, the last log image is the failing test.
There is no (xhr) 200 /productmanagement/api/v1/articles/get showing there.
It goes straight from api/v1/subscriptionfeatures/get to api/v1/vatcodes/get, but in the first test the api/v1/articles/get was between those two calls.
If it occurs later in the screenshot, add an increased timeout to catch it (the same intercept can use the longer timeout in both tests, but it won't delay the first test).
This may mean you have found a bug in the app - it seems that a "Duplicate" action should have the same POSTs as an "Add" action.
Have you resolved this?
I'm using this config:
Given('a GraphQL service error is thrown', () => {
cy.intercept({ method: 'POST', url: '/uat/graphql', times: 1 }, { forceNetworkError: true });
});
With times: 1. But the interception does not block the request now.
I found times in the docs.
I have an application which scans 2D barcodes then retrieves data from the URLs provided by the codes. In the event that the user loses connection to the internet, the application begins to store the URLs via AsyncStorage. The issue is, I need to implement a listener that upon regaining an internet connection, the application begins a given method. Are there any recommended ways to go about implementing a connection listener such as this?
Edit:
I have tried using a NetInfo EventListener however I am not sure if I'm using it incorrectly, as it always calls the passed function, even when the internet status hasn't changed.
_connectionHandler = (e) => {
this.setState({ cameraActive: false })
NetInfo.getConnectionInfo().then((connectionInfo) => {
if (connectionInfo.type === "none"){
console.log("No internet")
dataArray.push(e.data)
let barcodeData_delta = {
data: dataArray
}
AsyncStorage.mergeItem(STORAGE_KEY, JSON.stringify(barcodeData_delta));
NetInfo.isConnected.addEventListener(
'connectionChange',
this._handleConnectionChange(e.data)
);
this.setState({ cameraActive: true })
} else {
console.log("Internet available -> Going to read barcode now")
this._handleBarCodeRead(e.data);
}
})
}
React Native has a NetInfo documentation, there you can see how to add a listener his connection changes, and do what you want when its called.
Add a Handler to isConnected property
NetInfo.isConnected.addEventListener(
'connectionChange',
_connectionHandler
);
A function that handles the change, just adjust your setState with the camera, I couldn't figure out when to call it.
_connectionHandler = (isConnected) => {
this.setState({ cameraActive: false })
if (!isConnected){
console.log("No internet")
dataArray.push(e.data)
let barcodeData_delta = {
data: dataArray
}
AsyncStorage.mergeItem(STORAGE_KEY, JSON.stringify(barcodeData_delta));
this.setState({ cameraActive: true })
} else {
console.log("Internet available -> Going to read barcode now")
this._handleBarCodeRead(e.data);
}
})
}
I have a function, that gets called twice by an observe function. As you can see, in the addMessage function, there is a check that if it is called with the same messge, it returns.
My problem is, that the addMessage function is getting called twice with the same message.
I think the reason is because it is being done quickly before the first message has been added, the second message is executed already.
What is the best way to synchronize the messages, that the first one is given time to save, before the second one is processed?
this.messages.observe({
changed: (newMessage, oldMessage) => this.addMessage(newMessage)
});
and
private addMessage(message: Message): void {
let foundMessage: Message = this.localMessageCollection.findOne({ _id: message._id });
if (foundMessage && foundMessage._id === message._id) {
console.log('addMessage(found): '+message._id+' '+message.content);
return;
}
console.log('addMessage: '+message._id+' '+message.content);
this.addLocalMessage(message);
this.chatsStorageService.addMessage(this.activeChat, message).then((messageData: Message) => {
let data = {
chat: this.activeChat,
messageString: this.messageString,
sendMessage: true
}
this.events.publish('messages:update', data);
});
}