Acces this.state variables in React - reactjs

I have the following code in react :
getInitialState: function() {
var state= {
data: {}
};
fetch(this.props.dataurl)
.then(function(response) {
return response.json();
})
.then(function(result) {
this.setState({data:result});
}.bind(this));
return this.state;
},
componentDidMount: function() {
console.log(this);
console.log(this.state);
}
So in the getInitialState function I initialize the state variable data with results from fetch and then I want to access the data variable in the second function componentDidMount.
The problem I have is that this.state returns the data object empty but when I try to log this I'm getting the data variable with the data in it.
So why I'm having this behavior and how can I solve it?

componentDidMount does not guarantee that the async fetch has been completed.
You should define componentDidUpdate that will be called when the state has been changed, so that you can do anything with that new data.
componentDidUpdate(object prevProps, object prevState)
See React Lifecycle.

Related

React setState {} not setting variable

I have the following code for a main view and user login:
export class MainView extends React.Component {
constructor(){
super();
this.state = {
array: [],
user: null,
userData: {},
};
}
setUserData(user) {
if (user) {
this.setState({ userData: user.user });
console.log(user.user);
console.log (userData); /* errors in console */
} else {
console.log('userData not set');
}
}
onLoggedIn(authData) {
this.setState({ user: authData.user.Username });
/* setting localstorage key:item */
// console.log(authData);
localStorage.setItem('token', authData.token);
localStorage.setItem('user', authData.user.Username);
this.getArrayObjects(authData.token);
this.setUserData(authData);
}
using Passport to get the auth data and token. I don't get why it will log user.user in the setUserData function but will log an "undefined" error for the userData variable in the same function. Any ideas?
You are trying to access an undefined variable.
userData is present inside the state.
So you should access it with this.state.userData.
But even it you write console.log(userData);, it will print {} because in React, setState is asynchronous. Meaning, the state is not updated immediately.
If you want to check whether state has been update or not, check it like this.
this.setState({ userData: user.user }, () => {
console.log(this.state.userData);
});
The, second parameter to setState is a function that is called once state is successfully updated. So, inside there you can see the value in console.
You can't access the state directly as a var. You need to access the state, and the the properties:
console.log(state.userData);
Also, in your code, when you print state.userData, probably you will see the old value, since the setState is an async function.

How to setState inside an api function

I have this code inside a class method:
ref
.orderByValue()
.equalTo(email)
.once("value", snapshot => {
if (snapshot.exists()) {
console.log(this);
this.setState({ signedUp: true });
} else {
ref.update({ [newKey]: email });
}
});
It's going to update my Firebase database on submit, unless the user has already signed up. A typeError says this.setState is not a function. But when I console log this, the console prints out the class. Logging this.state also prints out the state.
How do I fix this? I want to update my state from inside this function.
The best solution is to use functional component.
However if you want to use class component, this is probably a binding issue.
So if this is triggered inside function named func, you should bind this function inside the constructor.
...
constructor(props) {
...
this.func = this.func.bind(this);
...
}
func() {
...
ref
.orderByValue()
.equalTo(email)
.once("value", snapshot => {
if (snapshot.exists()) {
console.log(this);
this.setState({ signedUp: true });
} else {
ref.update({ [newKey]: email });
}
});
...
}
If you don't want to bind this function, you should use arrow function.
func = () => {
...
}

How to update a react component on a socket.io event

Following advice from this question on forcing a component to update, I wrote the following code to update the component when my socket connection receives a broadcast.
constructor(props) {
super(props)
this.state = { key: 0 };
}
componentWillMount(){
const socket = io()
socket.on('data', function(data) {
// Do stuff with the data
this.setState({ key: Math.random() })
})
}
But I get the error
Uncaught TypeError: this.setState is not a function
I understand that this is because this is referring to something different when It's inside the socket.on() function but I don't know a way around it.
I also tried
constructor(props) {
super(props)
this.state = { key: 0 };
this.update = this.update.bind(this);
}
componentWillMount(){
const socket = io()
socket.on('data', function(data) {
// Do stuff with the data
update()
})
}
update(){
this.setState({ key: Math.random() })
}
But then I get the error
Uncaught ReferenceError: update is not defined
Does anyone have a better way to update my component or a fix for my current implementation?
You loose the context of this unless you do it as an arrow function:
socket.on('data', (data) => {
Also as you are using a class component you don't need that setState trick you can do this.forceUpdate().

this.setState does not update state

I'm trying to use this.setState within handleFormSubmit however this.setState isn't updating and I'm not sure why. If I run console.log(updatePosition) before this.setState I can that all the data is there. What am I missing? I use similar code for handleChange and I don't have problems.
constructor(props) {
super(props);
let uniqueId = moment().valueOf();
this.state = {
careerHistoryPositions: [{company: '', uniqueId: uniqueId, errors: {} }],
};
this.handleFormSubmit = this.handleFormSubmit.bind(this);
}
handleFormSubmit(event) {
event.preventDefault();
const { careerHistoryPositions } = this.state;
const updatePosition = this.state.careerHistoryPositions.map((careerHistoryPosition) => {
const errors = careerHistoryValidation(careerHistoryPosition);
return { ...careerHistoryPosition, errors: errors };
});
console.log(updatePosition)
this.setState({ careerHistoryPositions: updatePosition });
}
Keep in mind that the state isn't updated immediately. If you want to check if it's updated use callback function. Something as follows:
this.setState({ careerHistoryPositions: updatePosition }, () => console.log(this.state.careerHistoryPositions);
From the docs :
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
Hope this helps.
You should show how you are calling handleFormSubmit chances are that it's bound to a Dom event. So this is not the class/component, instead if you console.log(this); you'll see that it's the Dom element, the form element.
To make your code work as intended, in your component constructor() method, add this to rebind the handler function to the react component's class method, and you'll have access to this.state and this.setState()
this.handleFormSubmit = this.handleFormSubmit.bind(this);

Accessing a promise with the componentDidMount

I'm accessing a promise that gets returned from my mock API. The React component looks like what you see below
import React from 'react';
import StudentListStatistics from './StudentListStatistics';
import StudentStatisticsApi from '../../api/mockStudentApi';
class AboutPage extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
studentsStatistics: []
};
}
componentDidMount() {
StudentStatisticsApi.getAllStudentsStatistics().then(
studentsStatistics => {
this.setState({
studentsStatistics: studentsStatistics
});
debugger;
}
);
console.log(this.state.studentsStatistics);
}
render() {
return (
<div>
<h2>Student Body Statistics</h2>
<StudentListStatistics studentsStatistics={this.state.studentsStatistics}/>
</div>
);
}
the mock API looks like this
class StudentApi {
static getAllStudentsStatistics() {
return new Promise((resolve, reject)=> {
setTimeout(()=> {
resolve(Object.assign([], studentsStatistics));
}, 1000);
});
}
I'm not sure why the this.state.studentsStatistics is always an empty array. If I step through the code then studentsStatistics array is being returned correctly from my mock API within the then callback.
Can someone point out what I might be missing.
The issue is two-fold:
getAllStudentsStatistics() is asynchronous, which means that it will eventually yield a result, but not immediately;
setState() is also "asynchronous", in that it won't change this.state immediately after it got called.
To work around that, and log the mocked data, you need to first wait for the promise to resolve, and then to also wait for setState to acknowledge that the state has changed (by passing it a callback function):
componentDidMount() {
let promise = StudentStatisticsApi.getAllStudentsStatistics();
promise.then(studentsStatistics => {
this.setState({
studentsStatistics: studentsStatistics
}, () => {
console.log(this.state.studentsStatistics);
}
});
}
I think this also means that your StudentListStatistics component will initially be rendered with an empty array as input. Only once the promise has been resolved will it receive the mocked data.

Resources