I have some function (Promise Resolve) when onChange handler
This function will fetch data array by PromiseValue, so I use .Then( result ) to get the array field and it work when I print result with console.log.
But the problem is on this.setState because always get error with error message : _this2.setState is not a function
getList(checkboxes['myCheckbox']).then(
result => {
this.setState({ CheckBox: result })
}
).catch( err => {
console.log(err)
});
thanks before
Without more of the component, I'd guess this is probably occurring because your onChange handler in your component isn't bound to the class instance. So when the onChange fires and the promise code is executed, this.setState will not be defined.
You could make it work if you:
bound the functions in the constructor of the class component, i.e. this.handleChange = this.handleChange.bind(this)
use the class fields syntax, i.e. const handleChange = () => { // your code };
converted the component to a functional component and used hooks, i.e. useState;
I could be wrong though as I don't have the full text of the code! Hope this helps :)
Inside Promise.then(). this always refers to the callback function itself. so there would be no setState function.
Try defining a new function to save the result into the state.
Related
First let me put my code here and I'll explain what's happening.
Parent.js
callback = (id) => {
this.setState({des: id});
console.log(this.state.des);
}
//on my render i have this when i call my child component
<Child callback={this.callback}/>
Child.js
handleChange = (event) => {
let des = event.target.value;
console.log(des);
this.props.callback(des);
};
When i console.logon my Child component, it returns the data that i want to pass, but when I do it in calbackon my Parent component, it returns <empty string> and i don't know why that's happening.
The reason this is happening is because setState is an async function. When you are trying to log this.state.des, the state would not have been set yet. If you want to console log your state to see if it has been set as expected, what you want to do is log it in the callback of this.setState (so it logs once we know state is set). Try something like the following in your parent.js :
callback = (id) => {
this.setState({des: id}, () => {
console.log(this.state.des);
});
}
see the React Docs for setState for more details
The call to setState is asynchronous and therefore you might not read the updated state if you are accessing it directly after calling setState. Because of this setState(updater[, callback]) actually exposes a callback which can be used for operations which depend on the state update being done. (This is explained in the react docs for setState.)
In your case, adjusting the callback function like this
callback = (id) => {
this.setState({des: id}, () => {
console.log(this.state.des);
});
}
should do the trick.
If you want to know more about the reasoning behind setState being asynchronous (even if it might be a bit confusing in the beginning, like in your case) you should check out this github issue and especially this comment.
This.setState() is working for almost all variables in my component, but not all.
When i try to set the state of a state variable the value does not change which is a very strange behavior.
this.setState({ notSaved: size }, () => {
console.log(this.state.notSaved, 'notSaved','size',size);
});
}
Result of the console.log
This is not a problem with the async behavior of the setSate, as you can see in the image below, because even if I console.log the value inside the callback, still unchanged. So this is not a duplicated question.
setState(updater[, callback])
use callback in setState to read new value. setState is async and you are expecting your state to be updated instantly. To make sure your state got updated, you would have to leverage on call back function passed in as second argument.
this.setState((state, props) => {
return { notSaved: size }
}, () => console.log(this.state.notSaved, 'updated notSaved'));
Detailed explanation by Dan on why is setState asynchronous?: https://github.com/facebook/react/issues/11527#issuecomment-360199710
Am try to pass the parameters to the state callback function, which has click event listener.
the parameters are not getting updated with the latest value, i always get the value which is set on first time click.
this is what i tried:
this.setState({ idx: i0 === this.state.idx ? null : i0 }, () => {
document.addEventListener('click', this.closeMenu.bind(null, i0, event));
});
closeMenu(i0, event) {
event.stopPropagation();
this.setState({ idx: null }, () => {
document.removeEventListener('click', this.closeMenu);
});
}
Am expecting the closeMenu function parameter i0 value should get always the latest value which is getting passed from parent function.
Some points to consider. Why do you setting up listeners in callbacks to setState, setState is async, who can know what can happens in this scenario ? If you not denouncing you button click (or how you trigger this functionality) what if listener will be initialized up multiple times ? Another point, you doing this in callback to setState, what if component will be unmounted in this point ? This is clearly a possibility. I think the right choice will be to setup listener in componentDidMount and remove it in componentWillUnmount. So it should be something like this:
componentDidMount(){
document.addEventListener('click', this.closeMenu);
}
componentWillUnmount(){
document.removeEventListener('click', this.closeMenu);
}
One more point, to consume props in closeMenu or anywhere just you arrow functions like so :
closeMenu = event => {
let i0 = this.props.i0;
event.stopPropagation();
...
}
This will save you from passing additional parameter as it will bind function to component context and allow you to access props directly. If you elaborate a bit on what you trying to achieve, i can try to explain better.
I'm trying to dynamically load properties into a React component. The properties are set in this.props.customObjects using fetch with async/await higher up in the component structure.
My CustomObject component (below) receives the properties in the form of a promise, but I fail in actually showing the fetched data in the component. I guess my component renders before the promise has resolved.
What's the best way to solve this? Can values from async/await be used in props, or do I have to use state?
My component:
export default class CustomObject extends React.Component {
getCustomObject = (elementType) => {
const customObject = this.props.customObjects[elementType];
console.log('customObject', customObject);
return customObject;
}
componentDidMount() {
const customObject = this.getCustomObject(this.props.element.elementType)
this.setState({ customObject });
}
render () {
return <div className={this.props.className} style={this.props.style}>
{this.state.customObject.name} // Gives TypeError: Cannot read property 'customObject' of null
</div>
}
}
The console log looks like:
If it's a promise you can say something like:
customObject.then((result) => {
this.setState({customObject: result})
})
As you're waiting for something, you need to use setState to get re-render. You should put in a fallback, in case the promise is still pending in your render() when it is first called.
There is a couple of ways to handle your situation. One of them is if this.getCustomObject(...) is returning a Promise, you can wait for it to resolve and then set the state.
For Example
this.getCustomObject(this.props.element.elementType).then((customObject) => {
this.setState({ customObject });
});
For more information about Promise please check here.
This would not completely solve your problem if you don't give a default value for this.state.customObject since its gonna be undefined till the Promise resolved. You can use something like {this.state.customObject ? this.state.customObject.name : 'Loading..'}
When a react component state changes, the render method is called. Hence for any state change, an action can be performed in the render methods body. Is there a particular use case for the setState callback then?
Yes there is, since setState works in an asynchronous way. That means after calling setState the this.state variable is not immediately changed. so if you want to perform an action immediately after setting state on a state variable and then return a result, a callback will be useful
Consider the example below
....
changeTitle: function changeTitle (event) {
this.setState({ title: event.target.value });
this.validateTitle();
},
validateTitle: function validateTitle () {
if (this.state.title.length === 0) {
this.setState({ titleError: "Title can't be blank" });
}
},
....
The above code may not work as expected since the title variable may not have mutated before validation is performed on it. Now you may wonder that we can perform the validation in the render() function itself but it would be better and a cleaner way if we can handle this in the changeTitle function itself since that would make your code more organised and understandable
In this case callback is useful
....
changeTitle: function changeTitle (event) {
this.setState({ title: event.target.value }, function() {
this.validateTitle();
});
},
validateTitle: function validateTitle () {
if (this.state.title.length === 0) {
this.setState({ titleError: "Title can't be blank" });
}
},
....
Another example will be when you want to dispatch and action when the state changed. you will want to do it in a callback and not the render() as it will be called everytime rerendering occurs and hence many such scenarios are possible where you will need callback.
Another case is a API Call
A case may arise when you need to make an API call based on a particular state change, if you do that in the render method, it will be called on every render onState change or because some Prop passed down to the Child Component changed.
In this case you would want to use a setState callback to pass the updated state value to the API call
....
changeTitle: function (event) {
this.setState({ title: event.target.value }, () => this.APICallFunction());
},
APICallFunction: function () {
// Call API with the updated value
}
....
this.setState({
name:'value'
},() => {
console.log(this.state.name);
});
The 1. usecase which comes into my mind, is an api call, which should't go into the render, because it will run for each state change. And the API call should be only performed on special state change, and not on every render.
changeSearchParams = (params) => {
this.setState({ params }, this.performSearch)
}
performSearch = () => {
API.search(this.state.params, (result) => {
this.setState({ result })
});
}
Hence for any state change, an action can be performed in the render methods body.
Very bad practice, because the render-method should be pure, it means no actions, state changes, api calls, should be performed, just composite your view and return it. Actions should be performed on some events only. Render is not an event, but componentDidMount for example.
Consider setState call
this.setState({ counter: this.state.counter + 1 })
IDEA
setState may be called in async function
So you cannot rely on this. If the above call was made inside a async function this will refer to state of component at that point of time but we expected this to refer to property inside state at time setState calling or beginning of async task. And as task was async call thus that property may have changed in time being. Thus it is unreliable to use this keyword to refer to some property of state thus we use callback function whose arguments are previousState and props which means when async task was done and it was time to update state using setState call prevState will refer to state now when setState has not started yet. Ensuring reliability that nextState would not be corrupted.
Wrong Code: would lead to corruption of data
this.setState(
{counter:this.state.counter+1}
);
Correct Code with setState having call back function:
this.setState(
(prevState,props)=>{
return {counter:prevState.counter+1};
}
);
Thus whenever we need to update our current state to next state based on value possed by property just now and all this is happening in async fashion it is good idea to use setState as callback function.
I have tried to explain it in codepen here CODE PEN
Sometimes we need a code block where we need to perform some operation right after setState where we are sure the state is being updated. That is where setState callback comes into play
For example, there was a scenario where I needed to enable a modal for 2 customers out of 20 customers, for the customers where we enabled it, there was a set of time taking API calls, so it looked like this
async componentDidMount() {
const appConfig = getCustomerConfig();
this.setState({enableModal: appConfig?.enableFeatures?.paymentModal }, async
()=>{
if(this.state.enableModal){
//make some API call for data needed in poput
}
});
}
enableModal boolean was required in UI blocks in the render function as well, that's why I did setState here, otherwise, could've just checked condition once and either called API set or not.