My calls to setState are doing nothing in the then of a fetch.
The class is declared like this:
export default class QuestionHolder extends React.Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = {
completed: false,
revealAnswer: false,
selectedAlternative: undefined,
submitting: false,
explanation: 'philosophy',
}
console.log('props', props)
console.log('state', this.state)
}
fetch(`/questions/${this.question.id}/submit`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
chosen: this.state.selectedAlternative.id,
}),
})
.then(response => response.json())
.then(data => {
console.log('data', data)
console.log('state before', this.state)
this.setState({
explanation: 'phil says so'
})
console.log('state after', this.state)
})
The fetch works fine (returns data and a 200 etc etc - this is legacy code I am updating), but the calls to console.log show that this.state never changes from the values set in the constructor.
State updates may be asynchronous and reading it immediately after setting it will not provide the correct result.
The right place to check for state change is either to pass callback as second argument to setState method
this.setState({
explanation: 'phil says so'
},() => {
console.log('Set the explanation state successfully')
})
or using componentDidUpdate lifecycle method.
componentDidUpdate(prevProps, prevState){
if (prevState.explanation !== this.state.explanation) {
console.log('Set the explanation state successfully')
}
}
Related
I'm trying to retrieve the updated state value in componentDidMount() to append that value as a parameter in fetching data from an API. I have created this handleChange() method from where I can update the state with current required values. The problem is componentDidMount() hits before the first rendering of page so it shows me null value if I try to fetch the state value. I do not know how can I mention that I want to fetch the value after second rendering.
constructor(props) {
super(props)
this.state = {
names: [],
equipments: [],
isLoaded: false,
inputValue: null,
siteID: ''
};
}
handleChange = selectedOption => {
let { inputValue, stateID } = this.state;
this.setState({ ...this.state, inputValue: selectedOption });
console.log(`Option selected: ${selectedOption.value}`);
let selectedElement = selectedOption.value;
let filteredID = stateData.filter(name => name.name == selectedElement)
.map((name) => {
return name.id
})
// console.log(filteredID[0]);
this.setState({ stateID: filteredID[0] })
localStorage.setItem(stateID, filteredID[0]);
};
ComponentDidMount() {
const token = localStorage.getItem("token");
console.log("Inside Component Drop Down =" + token);
// let stateid = this.state.stateID;
// console.log("stateid" + stateid);
let stateid = 23301;//
let url2 = `https://applicaiton/api/helpdesk/get_personID/?stateid=${stateid}`;
fetch(url2, {
method: "GET",
headers: { "Content-Type": "application/json", "Authorization": `Token ${token}` },
credentials: "same-origin"
})
.then((results1) => {
return results1.json();
}).then(data2 => {
this.setState({
isLoaded: true,
equipments: data2,
})
});
}
Rather than componentDidMount, consider using the React lifecycle method componentDidUpdate as follows:
componentDidUpdate(prevProps, prevState, snapshot) {
// Make comparison between this.state and prevState
// as necessary to retrieve proper value
}
Boolean is not a function react-native
When ever fetching API in react native from device its giving Boolean is not a Function
//Fetching a get api
fetch('http://dummy.restapiexample.com/api/v1/employees', {
method: 'GET'
})
.then((response) => response.json())
.then((responseJson) => {
console.log(responseJson);
})
.catch((error) => {
console.error(error);
});
variable declaration was wrong ,
this.state ={
isLoading:Boolean = true
}
////instead of that we should be
this.state ={
isLoading: true
}
I able to get the right data from my API node.js server. however when i try to setstate the object to render it it keeps returning null
i tried to use spread operator before the response but it still not working
import React, { Component } from "react";
import axios from "axios";
class Profile extends Component {
constructor(props) {
super(props);
this.state = {
UserData: null,
isLoading: false,
error: null
};
}
componentDidMount() {
this.setState({ isLoading: true });
axios
.get(
`http://localhost:5000/api/v1/profile/${this.props.match.params.platform}/${this.props.match.params.gamertag}`
)
.then(response => {
console.log(response.data);
})
.then(response => {
this.setState({
UserData: response.data,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
render() {
const { isLoading, UserData } = this.state;
if (isLoading) {
return <p>Loading ...</p>;
}
console.log(UserData);
return <div>{UserData}</div>;
}
}
export default Profile;
when i try to log. the UserData log "null", but the "console.log(response.data)" works fine so it have to do something with the setState
when you chain data method like .then(), the following chained methods automatically receive value returned by the previous function.
getData
.then(res => console.log(res))
console.log itself will return nothing, thus the following .then() method will receive nothing.
getData
.then(res => console.log(res))
.then(data => console.log(data))
So if you do this, the second console.log() will log null.
You can fix it by returning something in your console.log step:
getData
.then(data => {
console.log(data);
return data;
})
.then(data => this.setState({ data: data }));
And the second console.log() will log properly.
You don't need two chain two then()'s, you can get the response and set the state after .then()
componentDidMount() {
this.setState({ isLoading: true });
axios
.get(
`http://localhost:5000/api/v1/profile/${this.props.match.params.platform}/${this.props.match.params.gamertag}`
)
.then(response => {
this.setState({
UserData: response.data,
isLoading: false
});
})
.catch(error => this.setState({ error, isLoading: false }));
}
Unable to this.setState() with fetch() response
Did fetch() inside form submit event handler, but unable to set the state from the fetch() callbacks
TypeError: Cannot read property 'setState' of undefined
...
constructor(props) {
super(props);
this.state = { deviceName: '', devices: [] };
this.handleChange = this.handleChange.bind(this);
this.handleSearchDevice = this.handleSearchDevice.bind(this);
}
componentWillMount() {
this.setState({
devices: this.props.devices
});
}
componentDidMount() {
}
componentWillReceiveProps(nextProps) {
this.setState({
devices: nextProps.devices
});
}
handleChange(event) {
this.setState({deviceName: event.target.value });
}
handleSearchDevice(event) {
console.log('Searching '+this.state.deviceName)
event.preventDefault();
//Get data from API
const url = 'device/name'
const data = { deviceName:this.state.deviceName}
fetch(url, { method: 'POST',
body: JSON.stringify(data),
headers:{ 'Content-Type': 'application/json' }
}).then(res => {
res.json().then(function(data) {
console.log('API Response: '+JSON.stringify(data))
try {
this.setState({devices: data.resp, deviceName: data.deviceName})
} catch(err) {
console.log('catch ' + err.stack)
this.callback1(data)
}
});
}).catch(error => {
console.error('Error:', error)
}).then(response => {
console.log('Success:', response)
});
}
callback1(data) {
this.setState({devices: data.resp, deviceName: data.deviceName})
console.log(data)
}
render() {
...
}
componentDidUpdate(prevProps) {
}
...
I expect to set the state from callbacks inside the event handler
Error screenshot
That is because you have not bound the function callback1 to this. So in your constructor you should bind it the same way you bind your other functions.
An alternative way is to make callback1 an arrow function instead so that it does not have to be bound. That would look like this:
callback1 = () => {
this.setState({devices: data.resp, deviceName: data.deviceName})
console.log(data)
}
How to setState() the response received from an AJAX Request so that I can display them in the page?
constructor(props)
{
super(props);
this.state = {
email: '',
first_name: '',
middle_name: '',
country: '',
country_code: '',
mobile_number: '',
gender: ''
}
}
componentDidMount()
{
store.dispatch(getUserProfile())
.then(() => {
const user = this.props.userProfile.userProfile && this.props.userProfile.userProfile.data.data;
this.setState({
email: user.email,
first_name: user.first_name
});
})
}
render()
{
return (
<div className="form-group col-sm-12">
<label htmlFor="email">Email*</label>
<input type="email" name="email" value={this.state.email || ''}/>
</div>
<div className="form-group col-sm-12">
<label htmlFor="email">First Name*</label>
<input type="email" name="email" value={this.state.first_name || ''}/>
</div>
)
}
Apparently, I can't use .then() with store.dispatch method.
Uncaught TypeError: _store2.default.dispatch(...).then is not a function
getUserProfile() action function
import axios from 'axios';
export function getUserProfile()
{
return function(dispatch)
{
dispatch(userProfileSuccess(false));
dispatch(userProfileError(null));
const request = axios
({
url: "http://testapi/auth/v1/user/details",
method: "get",
headers: {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + localStorage.getItem('access_token')
}
})
.then(function(response) { dispatch(userProfileSuccess(response)); })
.catch(function(error) {
console.log(error)
});
return {
type: 'USER_PROFILE_SUCCESS',
payload: request
}
};
}
function userProfileSuccess(userProfile)
{
return {
type: 'USER_PROFILE_SUCCESS',
userProfile: userProfile
};
}
function userProfileError(userProfileError)
{
return {
type: 'USER_PROFILE_ERROR',
userProfileError: userProfileError
};
}
export default getUserProfile;
In the AJAX call, I tried:
.then(function(response) {
return new Promise((resolve) => {
dispatch(userProfileSuccess(response));
resolve();
});
})
but the console reports the same error.
Is there a callback that I can pass to store.dispatch? What is the correct approach to this?
You can add a callback in componentDidMount()
componentDidMount()
{
store.dispatch(getUserProfile(), () => {
const user = this.props.userProfile.userProfile && this.props.userProfile.userProfile.data.data;
this.setState({
email: user.email,
first_name: user.first_name
});
})
}
This may not run exactly same, I just want to give you an idea how to add callback using arrow function so that you don't need to use then.
As you are using redux then your redux store should keep track about when the api call is in progress or has completed or caught some error. So instead of passing any callback or promise, you should dispatch an action for each event like processing, success, error etc (which you are already doing in getprofile function). Though i would say you nicely distinguish between process, success, error. For example you getprofile method should roughly look like this
export function getUserProfile() {
return function (dispatch) {
dispatch(userProfileProcessing())
const request = axios({
url: "http://testapi/auth/v1/user/details",
method: "get",
headers: {
'Content-Type': 'application/json',
'Authorization' : 'Bearer ' + localStorage.getItem('access_token'),
},
})
.then(function (response) {
dispatch(userProfileSuccess(response))
})
.catch(function (error) {
dispatch(userProfileError(response))
console.log(error)
});
};
}
It is just what i prefer. If you want it your way, that is also fine.
Now everytime you dispatch any action, redux will update the reducer state. So thats the place where you can set/reset some flag to make the component aware of what is going on with api call. So your reducer might look like this:
// getUserProfileReducer.js
userProfileReducer = (state = {}, action) => {
switch (action.type) {
case 'USER_PROFILE_PROCESSING':
return {
...state,
processing: true,
success: false,
fail: false,
userProfile: null,
}
case 'USER_PROFILE_SUCCESS':
return {
...state,
processing: false,
success: true,
fail: false,
userProfile: action.userProfile,
}
case 'USER_PROFILE_Error':
return {
...state,
processing: false,
success: false,
fail: true,
userProfile: null,
}
}
}
Now all you need to do is to access this state from you component so that you can take necessary action according to that. For that you can user mapStateToProps function which convert the redux state to prop of the component.
constructor(props) {
super(props)
this.state = {
email: '',
first_name: '',
middle_name: '',
country: '',
country_code: '',
mobile_number: '',
gender: '',
}
}
componentWillReceiveProps(newProps) {
if (newProps.userProfileStatus.success) {
// The success flag is true so set the state
const user = newProps.userProfileStatus
this.setState({
email: user.email,
first_name: user.first_name,
})
}
else if (newProps.userProfileStatus.processing) {
// Api call is in progress so do action according to that like show loader etc.
}
}
componentDidMount() {
store.dispatch(getUserProfile())
}
render() {
return (
...
)
}
const mapStateToProps = (state) => {
return {
userProfileStatus: state.userProfileReducer,
}
}
Redux stores the state in the Redux store, separately from the React component state (think setState). You are almost there. What you need to do is guide the result data from the async dispatch to the redux store and then to your local component state. Steps 3 and 4 below.
Dispatch an async action to fetch the data.
Dispatch an action from within the promise to populate the redux state.
Write a reducer that intercepts the action and populates the redux state.
Connect your local component state with the redux state by using the connect function.