Cannot call setState in callback - reactjs

I am new to React, so I know I might not be doing everything right.
I am trying to call setStatus inside of a callback for onreadystatechange but getting an error in the browser. The code does an AJAX call to the server to see if a proposed username is already in the database. Upon return the onreadystatechange is executed. There I am trying to set the new error message for the username if it already exists.
Here is my state data:
const initialState = {
firstname: "",
lastname: "",
username: "",
password: "",
email: "",
firstnameError: "",
lastnameError: "",
usernameError: "",
passwordError: "",
emailError: ""
};
class SignUpForm extends Component {
constructor() {
super();
this.state = initialState;
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.checkUserName = this.checkUserName.bind(this);
this.checkEmail = this.checkEmail.bind(this);
this.validateForm = this.validateForm.bind(this);
}
This is where I update state in a change handler for fields in the form:
handleChange(e) {
let target = e.target;
let value = target.value;
let name = target.name;
if (name === "username") {
value = value.toUpperCase();
}
this.setState({
[name]: value
});
}
This is the routine where I am getting an error in the browser. The lines where the error occurs is marked with a comment on the line below.
checkUserName() {
let usernameError = "";
let element = "";
let checkNameResponse = "";
let checkNameMessage = "";
let request = "";
let url = "";
let userName = "";
let requestData = "";
let checkNameResponseJSON = "";
request = new XMLHttpRequest();
if (request == null) alert("Unable to create checkDBForUSerNameRequest");
else {
url = "/php/CheckUsername.php";
userName = escape(document.getElementById("username").value);
requestData = JSON.stringify({
username: userName
});
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200) {
checkNameResponseJSON = JSON.parse(request.responseText);
checkNameResponse = checkNameResponseJSON.returncode;
checkNameMessage = checkNameResponseJSON.message;
element = document.getElementById("SignupIcon");
element.className = "displayIcon";
if (checkNameResponse === 0) {
element = document.getElementById("SignupIconFile");
element.src = "/images/YesButton.png";
element.alt = "Available";
this.setState({ usernameError: "" });
} else {
element = document.getElementById("SignupIconFile");
element.src = "/images/NoButton.png";
element.alt = "Not Available";
usernameError = checkNameMessage;
this.setState({ usernameError: usernameError }); // BROWSER ERROR
}
}
};
request.open("POST", url, true);
request.setRequestHeader("Content-Type", "application/json");
request.send(requestData);
}
}
This is the text from the browser error:
0: Object doesn't support property or method 'setState'
Any ideas why this happens or how to fix?

The error is because you are using request.onreadystatechange = function() {} instead of request.onreadystatechange = () => {} or request.onreadystatechange = function() {}.bind(this)
When you use function(), the scope of this changes.
Remember that function() {} and () => {} are not the same.
but function(){}.bind(this) and () => {} are the same.

You need to bind the onreadystatechange function using .bind or arrow function since when the function is executed it needs to access the correct this value referring to the class context.
request.onreadystatechange = function() {
// all logic here
}.bind(this)
or
request.onreadystatechange = () => {
//all logic here
}

Related

Converting react class to function component (openvidu)

I got tutorial code from openvidu which is built with class components.
this.state = {
mySessionId: 'SessionA',
myUserName: 'Participant' + Math.floor(Math.random() * 100),
session: undefined,
mainStreamManager: undefined,
publisher: undefined,
subscribers: [],
};
joinSession() {
this.OV = new OpenVidu();
this.setState(
{
session: this.OV.initSession(),
},
() => {
var mySession = this.state.session;
console.log(mySession)
mySession.on('streamCreated', (event) => {
var subscriber = mySession.subscribe(event.stream, undefined);
var subscribers = this.state.subscribers;
subscribers.push(subscriber);
...many other functions...
}
I'm trying to convert this class component into function component.
I think I have done something wrong here, because I can't get this "OV.initSession()" thing
in session state.
console.log(OV.initSession()) - it should look like this when it's working correctly
What am i doing wrong?
my code:
const [session, setSession] = useState(undefined);
const joinSession =()=>{
let OV = new OpenVidu();
let newsession = OV.initSession();
setSession(newsession);
console.log(newsession);
let mySession = session;
console.log(mySession);
mySession.on('streamCreated', (event) => {
let subscriber = mySession.subscribe(event.stream, undefined);
subscribers.push(subscriber);
setSubscribers(subscribers);
console.log(subscribers);
});
...many other functions...
}

React Props doesn't update the state

What is wrong with the below code in react? I see that the props are shown up with a delay (based on console.log) but other than that I don't see what is the issue here.
state = {
data: {
firstName: "",
lastName: "",
email: "",
},
errors: {},
};
componentDidMount() {
const { firstName, lastName, email } = this.props.user;
var changeData = { ...this.state.data };
changeData.firstName = firstName || "";
changeData.lastName = lastName || "";
changeData.email = email || "";
if (firstName) {
console.log(changeData);
this.setState({ data: { changeData } });
}
}
I don't see the data being changed in the state....
changeData is already an object no need to wrap with {} change your code like below
this.setState({ data: changeData });

Not able to add chat message using Sockjs in react native

I am facing issue in adding chat message in my Flatlist because I am not able to call any function inside stompClient.subscribe ()
My code is as following :
dummy='dummyurl';
sock = new SockJS(this.dummy);
componentDidMount() {
var that = this;
this.sock.onConnect =(e) =>{
console.log("connected")
}
this.sock.onopen = function() {
console.log('open');
var dummy2='dummyurl';
var sock2 = new SockJS(dummy2);
let stompClient = Stomp.over(sock2);
stompClient.heartbeat.outgoing = 20000;
stompClient.heartbeat.incoming = 0;
stompClient.connect({}, (frame) => {
stompClient.subscribe('xyz/getChat', (messageOutput) =>{
var mymessage =JSON.parse(messageOutput.body).message;
this.state = {
incomingchatMessage: "",
chatMessages:[]
}
that.setState({incomingchatMessage:mymessage}) //getting issue setState not found
console.log(this.state)
});
});
};
this.sock.onmessage =(e) =>{
console.log('message', e);
alert("on message calledj")
};
this.sock.onmessage = evt => {
console.log("erve");
}
}
In this code I am able to get net message inside new_message variable but not able to update state value here .
Any solution of this condition .

React Native updating data onState variable change

i am trying to execute a function that update the Users data whenever the user come at the end of the Array like so :
componentDidUpdate = () => {
if (this.state.isLoading == false && this.state.Users.length == this.state.currentIndex) {
this.loadUsers()
}
}
Here is the loadUsers() function
loadUsers = () => {
Users = [
]
var query = firebase.database().ref("users").orderByKey();
query.once("value")
.then((snapshot) => {
snapshot.forEach((childSnapshot) => {
// key will be "ada" the first time and "alan" the second time
var key = childSnapshot.key;
// childData will be the actual contents of the child
var childData = childSnapshot.val();
getFilmDetailFromApi(childData.filmID).then(data => {
km = this.getDistance(this.state.userData.latitude, this.state.userData.longitude, childData.latitude, childData.longitude)
photo0 = null;
photo1 = null;
photo2 = null;
photo3 = null;
photo4 = null;
photo5 = null;
if(childData.photo[0]) {
photo0 = {uri: childData.photo[0]}
}
if(childData.photo[1]) {
photo1 = {uri: childData.photo[1]}
}
if(childData.photo[2]) {
photo2 = {uri: childData.photo[2]}
}
if(childData.photo[3]) {
photo3 = {uri: childData.photo[3]}
}
if(childData.photo[4]) {
photo4 = {uri: childData.photo[4]}
}
if(childData.photo[5]) {
photo5 = {uri: childData.photo[5]}
}
var filmImage = data.poster_path
Users.push({ id: key, uri: photo0, uri1: photo1, uri2: photo2, uri3: photo3, uri4: photo4, uri5: photo5, nom: childData.name, age:childData.age, film: filmImage, description: childData.description, distance: km })
this.setState({ users: key });
})
this.setState({Users: Users});
});
this.setState({isLoading: false});
this.setState({currentIndex: 0});
});
}
This work like the tinder cards, when i swipe them all (this.state.Users.length == this.state.currentIndex) become true and it render the loading screen and from there i want to re-run the loadUsers function to get new users but when i run the app i get : "Maximum update depth exceeded".
LoadUsers loads user and sets the state, which causes a rerender of loading screen. Then the loading screen run the LoadUser again trigers the flow again.
Edit1:
At least, you'd have to have some logic to update the status of loading:
Check the modified code:
loadUsers = () => {
Users = [
]
var query = firebase.database().ref("users").orderByKey();
query.once("value")
.then((snapshot) => {
snapshot.forEach((childSnapshot) => {
// key will be "ada" the first time and "alan" the second time
var key = childSnapshot.key;
// childData will be the actual contents of the child
var childData = childSnapshot.val();
this.setState({isLoading: true});
getFilmDetailFromApi(childData.filmID).then(data => {
km = this.getDistance(this.state.userData.latitude, this.state.userData.longitude, childData.latitude, childData.longitude)
photo0 = null;
photo1 = null;
photo2 = null;
photo3 = null;
photo4 = null;
photo5 = null;
if(childData.photo[0]) {
photo0 = {uri: childData.photo[0]}
}
if(childData.photo[1]) {
photo1 = {uri: childData.photo[1]}
}
if(childData.photo[2]) {
photo2 = {uri: childData.photo[2]}
}
if(childData.photo[3]) {
photo3 = {uri: childData.photo[3]}
}
if(childData.photo[4]) {
photo4 = {uri: childData.photo[4]}
}
if(childData.photo[5]) {
photo5 = {uri: childData.photo[5]}
}
var filmImage = data.poster_path
Users.push({ id: key, uri: photo0, uri1: photo1, uri2: photo2, uri3: photo3, uri4: photo4, uri5: photo5, nom: childData.name, age:childData.age, film: filmImage, description: childData.description, distance: km })
this.setState({ users: key, isLoading: false });
})
this.setState({Users: Users});});
this.setState({currentIndex: 0});
});
}
You are getting this error because you are trying to set state inside a loop and the maximum limit has reached to rerender a view once there is change in state. Try to set Users this.setState({ Users: Users }); once the foreach loop ends(outside of loop). this.setState({ users: key }); this may also need the similar approach.

how to update state with in method in reactjs

There is a state and i want to update this state with in a function.
In function i declared a variable and i want to update state with this variable.
this.state = {
RequestData : [],
searchopen : false,
capturedImage : ""
}
screenShot = () => {
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
// Get base64URL
var base64URL = canvas.toDataURL('image/jpeg').replace('image/jpeg', 'image/octet-stream');
});
this.setState({capturedImage: base64URL})
}
You need to put setState in function that get base64URL and bind your screenShot function to use this.setState:
// constructor
constructor(props) {
super(props);
this.state = {
RequestData: [],
searchopen: false,
capturedImage: ""
}
this.screenShot = this.screenShot.bind(this);
}
screenShot = () => {
html2canvas(document.body).then(function (canvas) {
document.body.appendChild(canvas);
// Get base64URL
var base64URL = canvas.toDataURL('image/jpeg').replace('image/jpeg', 'image/octet-stream');
this.setState({ capturedImage: base64URL })
}.bind(this)); // bind here also
}
The problem here is you are doing the state update outside the callback function. Since this is a asynchronous task, your method will not work. Try this:
screenShot = () => {
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
// Get base64URL
var base64URL = canvas.toDataURL('image/jpeg').replace('image/jpeg', 'image/octet-stream');
this.setState({capturedImage: base64URL})
});
}
Not pretty but should work
this.state = {
RequestData : [],
searchopen : false,
capturedImage : ""
}
screenShot = () => {
var setState = newStore => this.setState((state, props) => ({ ...newStore })); // define the function outside
html2canvas(document.body).then(function(canvas) {
document.body.appendChild(canvas);
// Get base64URL
var base64URL = canvas.toDataURL('image/jpeg').replace('image/jpeg','image/octet-stream');
setState({capturedImage: base64URL}) // use here
});
}

Resources