pusher subscription with React-native, setState update - javascript

I'm trying to implement function with Pusher-JS in a React-native app but having a difficult time to appropriately update the state of a variable in this app.
Out side of main class, I define
var pusher = new Pusher('xxxxxxxsome number and code');
Inside the main class component, I define a function
test() {
var channel = pusher.subscribe('test_channel');
console.log('ddddddd');
channel.bind('my_event', function(data) {
if (data != null) {
if (data.message != null){
console.log(data.message);
this.setState({
message_box: data.message
});
}
}
}.bind(this));
}
and I put test() under render() function like the below.
render() {
this.what();
...
The app freezes when I push a text message (from pusher testing server) to 'test_channel'(in the mobile app). When I see the log in Chrome console, the console.log('dddddd') and console.log(data.message) keeps being scrolled down very quickly ( seems like test() is re-rendered a lot ).
I tried to take test() outside of render() but I don't know how to separately put and update the state variable outside of main component.
Is there way that I can stabilize this function or way to setState outside of main component ( this )?
Please share any idea with me!
Best

Is your test function being called every time on render? Pusher#bind adds a new callback to its registry whenever it gets called. It could be that whenever a message comes in, this function is being called multiple times, leading to more callbacks in the registry.
Perhaps try binding to an event once, perhaps in getInitialState?

Related

How to get document element when I use external script in react?

I'm using React to build my project. I made a chat button by using external script. And I'd like to disappear that button in some specific pages. I can't use a ref to access that button's element So I used document.getElementById.
But my problem is my code sometimes returns error. (I think when my code runs, chat button didn't run by external script.) How can I solve this problem?
useEffect(() => {
//access chat button element by using document.getElementById
const chatBtn = document.getElementById('ch-plugin-launcher');
//if it doesn't exist in current page, it returns.
if (!chatBtn) {
return;
}
//if a button exists, it will be hide.
chatBtn.classList.add('hide');
return () => {
chatBtn.classList.remove('hide');
};
}, []);
I think the error in return of useEffect. You return a function, which can be called at any time whenever the chat button does not exist. Add check for existing on the chat button in the useEffect return function. Other code looks well.
useEffect(() => {
// Your code
return () => {
document.getElementById('ch-plugin-launcher')?.classList?.remove('hide');
};
});
I think #0x6368656174 answwer is correct.
Just for more clarification why:
When exactly does React clean up an effect? React performs the cleanup
when the component unmounts. However, as we learned earlier, effects
run for every render and not just once. This is why React also cleans
up effects from the previous render before running the effects next
time. We’ll discuss why this helps avoid bugs and how to opt out of
this behavior in case it creates performance issues later below.
Source: https://reactjs.org/docs/hooks-effect.html#example-using-hooks-1

Best way to run a function when page refresh

I am trying to call a function when the page is refreshed. I adding a state if the page is rendered with the data I got from my backend end but I get an warning message "Warning: Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state." Even though it works fine (except with the warning message), I dont think this is the best, most efficient way to do it?
If this is the best, most efficient way, how do I fix the waring message?
function Demo() {
constructor(){
this.state = {
username: "unknown",
rendered: false,
}
this.renderUserProfile = this.renderUserProfile.bind(this);
}
update(){
//code to retrieve data from backend node.js *
this.setState({ username: data });
this.setState({ rendered: true });
}
render(){
if (!this.state.rendered) {
this.update();
}
return (<p>demo</p>)
}
}
Thank you for your help!
Do never change state inside render, because every state (or prop) change will call render again. That is what the warning is telling you: you risk having infinite loops.
There is no need of a state param for "rendered", because your component will call render anyway as soon as this.setState({username: data}); executes. If you want something to happen then, add it in update just after the setState line.
Now let's imagine that you still really want it. If you don't want your component to render when the rendered state changes, then just don't use the React Component state, but any standard class attribute:
class MyComponent extends React.Component {
rendered = false
...
render() {
this.rendered = true
....
}
}
Just be aware that this looks super wrong (and useless) since it tries to go around what the React framework is good at.
Finally, from this code there is no way to know how you intend you have new data coming in. If it is an Ajax call, then you will call this.update with that data in the callback of your Ajax call - certainly not in render.

this.props.history.push is working just after refresh

I have a react-big-calendar, which has a button when I click on it, his modal will appear, and I have also a calendar icon button which redirects me to another URL /planning.
I have a dialog (AjouterDisponibilite) and I call it with the ref on my component Agenda.
And the calendar icon has an onClick event which I try:
this.props.history.push('/planning');
but when I run it and I click on the icon, the URL is directed correct, but I get an error as shown below:
TypeError: Cannot read property 'handleAjouter' of null and
Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method
and after I refresh this page, it works.
My code is: https://codesandbox.io/s/xw01v53oz
How can I fix it?
The problem is improper use of Refs. Refs are designed to keep references to DOM-elements and component instances, not their functions.
Components with a ref prop will assign themself to the value of the prop (or call the function with itself as an argument) when they are mounted, and they will do the same with a null reference when they are unmounted.
With this knowledge, you have to change your code of the Agenda component as follows:
ModalAjout = (ref) => {
if (ref) {
this.ajout = ref.handleAjouter;
} else {
this.ajout = null;
}
}
ModalAjoutb = (ref) => {
if (ref) {
this.ajoutb = ref.handleAjouter;
} else {
this.ajoutb = null;
}
}
The best way to solve this you can add this code:
history.pushState(null, document.title, location.href);
What is doing? It's initializing the history with one value after it was loaded the first time, then you don't need to do the refresh.
In my case I did this while I was developing an Android App and had some modals that I wanted to close with the back button to provide a more native experience.
I fix it, by adding:
<AjouterDisponibilite ref={(evt) => this.ModalAjout({evt})} start={this.state.startDisponibilite} end={this.state.endDisponibilite} date={this.state.dateDisponibilite}/>
<AjouterDisponibilite ref={(evt) => this.ModalAjoutb({evt})} />
The method ModalAjout must have a parameter.

Incrementing the Material Design Lite Progress bar with React

I've got MDL running with React at the moment and it seems to be working fine at the moment.
I've got the Progress Bar appearing on the page as needed and it loads up with the specified 'progress' on page load when either entering in a number directly:
document.querySelector('#questionnaireProgressBar').addEventListener('mdl-componentupgraded', function() {
this.MaterialProgress.setProgress(10);
})
or when passing in a number via a Variable:
document.querySelector('#questionnaireProgressBar').addEventListener('mdl-componentupgraded', function() {
this.MaterialProgress.setProgress(value);
})
It stops working after this though. I try to update the value via the Variable and it doesn't update. I've been advised to use this:
document.querySelector('.mdl-js-progress').MaterialProgress.setProgress(45);
to update the value but it doesn't work. Even when trying it directly in the console.
When trying via the Console I get the following Error:
Uncaught TypeError: document.querySelector(...).MaterialProgress.setProgress is not a function(…)
When I try to increment the value via the Variable I get no errors and when I console.log(value) I am presented the correct number (1,2,3,4...) after each click event that fires the function (it fires when an answer is chosen in a questionnaire)
What I want to know is if there's something obvious that I'm missing when using MTL and React to make components to work? There was an issue with scope but I seem to have it fixed with the following:
updateProgressBar: function(value) {
// fixes scope in side function below
var _this = this;
document.querySelector('#questionnaireProgressBar').addEventListener('mdl-componentupgraded', function() {
this.MaterialProgress.setProgress(value);
})
},
In React I've got the parent feeding the child with the data via props and I'm using "componentWillReceiveProps" to call the function that updates the progress bar.
I've used the "componentDidMount" function too to see if it makes a difference but it still only works on page load. From what I've read, it seems that I should be using "componentWillReceiveProps" over "componentDidMount".
It's being fed from the parent due to components sending data between each other. I've used their doc's and some internet help to correctly update the parent function to then update the progress bar in the separate component.
updateProgressBarTotal: function(questionsAnsweredTotal) {
this.props.updateProgressBarValue(questionsAnsweredTotal);
}
The parent function looks like the following (I think this may be the culprit):
// this is passed down to the Questions component
updateProgressBarTotal: function(questionsAnsweredTotal) {
this.setState({
progressBarQuestionsAnswered : questionsAnsweredTotal
})
}
I can post up some more of the code if needed.
Thank you
Looks I needed a fresh set of eyes on this.
I moved the function to the child of the parent. It seems that using document.querySelector... when in the parent doesn't find the element but when it's moved to the child where I do all the question logic it seems to be fine. It increments the progress correctly etc now :)
// goes to Questionnaire.jsx (parent) to update the props
updateProgressBarTotal: function(questionsAnsweredTotal) {
// updates the state in the parent props
this.props.updateProgressBarValue(questionsAnsweredTotal);
// update the progress bar with Value from questionsAnsweredTotal
document.querySelector('.mdl-js-progress').MaterialProgress.setProgress(questionsAnsweredTotal);
},
I had same problem in angular2 application.
You don't necessary need to move to the child component.
I found after struggling to find a reasonable fix that you simply have to be sure mdl-componentupgradedevent already occurred before being able to use MaterialProgress.setProgress(VALUE). Then it can be updated with dynamic value.
That is why moving to the child works. In the parent component mdl-componentupgraded event had time to occur before you update progress value
My solution for angular2 in this article
Adapted in a React JS application :
in componentDidMount, place a flag mdlProgressInitDone (initiated to false) in mdl-componentupgraded callback :
// this.ProgBar/nativeElement
// is angular2 = document.querySelector('.mdl-js-progress')
var self = this;
this.ProgBar.nativeElement.addEventListener('mdl-componentupgraded', function() {
this.MaterialProgress.setProgress(0);
self.mdlProgressInitDone = true; //flag to keep in state for exemple
});
Then in componentWillReceiveProps test the flag before trying to update progress value :
this.mdlProgressInitDone ? this.updateProgress() : false;
updateProgress() {
this.ProgBar.nativeElement.MaterialProgress.setProgress(this.currentProgress);
}
After attaching the progress bar to the document, execute:
function updateProgress(id) {
var e = document.querySelector(id);
componentHandler.upgradeElement(e);
e.MaterialProgress.setProgress(10);
}
updateProgress('#questionnaireProgressBar');

React.js Update component passed as argument

I'm developing web xmpp chat using React.js and Strophe.js, and I run in quite problem.
XMPP connection is stored single object, but what is tricky, connection status change is handled by class, where i can store the true handler (because displaying connection status will have 2 different views, and Strophe.OnConnection handler cannot be changed after initialization)
What I want to achieve is to pass component to a function, to render itself with new props, something like it
var statusHandler = {
handler:"",
setStatus: function(status) {
React.render(<this.handler value=status/>, dom);
}
}
var firstContainer = React.render(<anotherComponent/>, dom);
statusHandler.handler = firstContainer;
XMPPConnection(login,pwd,statusHandler.setStatus);
//now changing component
var secondContainer = React.render(<anotherComponent/>, dom);
statusHandler.handler = secondContainer;
Or its possible to define callback on component,and pass it as argument (but not a static function)
One idea that may clean up your code would be to emit an event when the connection status changes. Then inside your react components inside componentDidMount listen to that event and set your state accordingly.
Something like this: http://jsfiddle.net/kb3gN/11114/

Categories

Resources