Cannot access state inside callback - reactjs

I've foreach function where I'm iterating the markers and calling getDetails function. At the end of getDetails, I'm calling callback function when all elements are processed. Inside callback I'm trying to access state property but I'm unable to access it. I've tried using bind method however error I'm getting is Uncaught TypeError: Cannot read property 'callback' of null.
export default class extends Component {
constructor(props) {
super(props);
this.state = {
markers: []
value: ''
}
handleChange(event) {
callback = () => {
console.log(this.state.markers);
}
markers.forEach((place,index) => {
.....
services.getDetails(request, (place, status) => {
if(place != null){
.....
}else{
}
itemsProcessed++;
if(itemsProcessed === markers.length) {
this.callback();
}
});
});
}
}

Declare callback function with arrow function like this
callback = () => {
console.log(this.state.markers);
}
And call it like this:
if(itemsProcessed === markers.length) {
this.callback();
}
Update::
You are not binding your handlechange function with class. So first you need to bind your handlechange function. A better way to do this is to use arrow function like this::
handleChange = (event) => {
Now if you want to keep your callback function out of handlechange function then you can do like this (Link of working example)::
callback = () => {
console.log(this.state.markers);
}
handleChange = (event) => {
...
markers.forEach((place,index) => {
services.getDetails(place, (place, status) => {
itemsProcessed++;
if(itemsProcessed === markers.length) {
this.callback();
}
});
});
}
Or if you want to keep your callback function inside handlechang function then you can do it like this(Link of working example)::
handleChange = (event) => {
let callback = () => {
console.log(this.state.markers);
}
...
markers.forEach((place,index) => {
services.getDetails(place, (place, status) => {
itemsProcessed++;
if(itemsProcessed === markers.length) {
callback();
}
});
});
}

Related

trying to an remove event listeners on state change

I'm trying to remove a mouseover/deviceorientatation event listener when a click on an object when it changes state.
I can addeventlisteners on load and when the state equals false.
I've added console.logs to make sure the useEffect gets to each part of the if statement, which it does, but I can't seem to remove the event listeners when the state is equal to true.
I understand that I need to have it return for clean-up, but no matter how much I try I cant remove any eventlistener.
I've added my useEffect code below, if you need any more of the code please let me know and I'll add it to the question.
Any help would be much appreciated.
useEffect(() => {
const noHover = window.matchMedia("(hover: none)");
if(!isOpen) {
if(noHover.matches) {
console.log('one')
window.addEventListener('deviceorientation', (event) => DeviceOrientation(event), true);
} else {
console.log('two')
window.addEventListener('mousemove', (event) => MouseOrientation(event), true);
}
} else {
if(noHover.matches) {
console.log('three')
window.removeEventListener('mousemove', (event) => MouseOrientation(event), true);
} else {
console.log('four')
window.removeEventListener('deviceorientation', (event) => DeviceOrientation(event), true);
}
}
return () => {
window.removeEventListener('mousemove', (event) => MouseOrientation(event), true);
window.removeEventListener('deviceorientation', (event) => DeviceOrientation(event), true);
} }, [isOpen]);
The useEffect returns a cleanup function that is called whenever the dependencies (isOpen) change and can remove the event handlers. However, the event handlers need to be the actual event handler that was used in addEventListener, not just an identical function.
You can achieve it by assigning the functions to variables according to your rules, and only adding/removing the listeners if the variable has been assigned a function.
useEffect(() => {
let deviceorientationHandler = null;
let mousemoveHandler = null;
if (!isOpen) {
const noHover = window.matchMedia("(hover: none)");
if (noHover.matches) {
deviceorientationHandler = (event) => DeviceOrientation(event);
} else {
mousemoveHandler = (event) => MouseOrientation(event);
}
}
if(deviceorientationHandler) window.addEventListener('deviceorientation', deviceorientationHandler, true);
if(mousemoveHandler) window.addEventListener('mousemove', mousemoveHandler, true);
return () => {
if(deviceorientationHandler) window.removeEventListener('mousemove', deviceorientationHandler, true);
if(mousemoveHandler) window.removeEventListener('deviceorientation', mousemoveHandler, true);
}
}, [isOpen]);
We can shorten it a bit more by assigning the entire event params to a variable (handler), and only adding or removing the event if handler has a value:
useEffect(() => {
let handler = null
if (!isOpen) {
const noHover = window.matchMedia("(hover: none)");
handler = noHover.matches ?
['deviceorientation', (event) => DeviceOrientation(event), true] :
['mousemove', (event) => MouseOrientation(event), true];
window.addEventListener(...handler);
}
return () => {
if (handler) window.removeEventListener(...handler);
}
}, [isOpen]);

Can't set state in a onClick function in React Hook

Hi i cant set state in a using a function i defined for onClick. All other lines are working except setting the state.
export default function SaleProducts(props) {
const [currentSelected, setSelected] = useState(props.location.state.pid);
useEffect(() => {
superagent
.post("url")
.set("Content-Type","application/x-www-form-urlencoded")
.send({"access_token":token})
.set('accept', 'json')
.end((error, response) => {
if(error){
console.log("Error")
}
else{
var json = response.body;
json.products.map((res) => {
var array = [res.title,"Not yet published",res.variants[0].price,<Button onClick={(event) => handleItemDeletion(event,res.id)}>Delete Item</Button>];
arr.push(array);
})
,[currentSelected]}
const handleItemDeletion = (event,id) =>{
event.preventDefault();
var cSelected = currentSelected.replace(id,'');
setSelected((currentSelected) => cSelected); //this is not working
console.log("Current Selected : ",currentSelected)
}
return(<arr>); //this is only for representation
OnClick function is getting called but only that setSelected line is not working. The state is not changing it is still like before.
You should pass value to setSelected, not a function. Something like this: setSelected(cSelected);
Setting the state is not correct. Try this,
export default function SaleProducts(props) {
const [currentSelected, setSelected] = useState(props.location.state.pid);
useEffect(() => {
superagent
.post("url")
.set("Content-Type","application/x-www-form-urlencoded")
.send({"access_token":token})
.set('accept', 'json')
.end((error, response) => {
if(error){
console.log("Error")
}
else{
var json = response.body;
json.products.map((res) => {
var array = [res.title,"Not yet published",res.variants[0].price,<Button onClick={(event) => handleItemDeletion(event,res.id)}>Delete Item</Button>];
arr.push(array);
})
,[currentSelected]}
const handleItemDeletion = (event,id) =>{
event.preventDefault();
var cSelected = currentSelected.replace(id,'');
setSelected(cSelected); // check here
console.log("Current Selected : ",currentSelected)
}
return(<arr>);

Why am I getting old value of state in the setState callback in react.js

I have a function to update the state and call another function to
update object value in the setState callback method.
I also added a debugger on the breakpoint for the setState callback
method, what I observe is that the value always is the old one.
updateContactPath(path, index) {
const { contactPaths } = this.state;
const { setFieldValue } = this.props;
contactPaths[index] = path;
this.setState(
{
contactPaths,
},
() => setFieldValue('contactPaths', contactPaths),
);
}
We can do something like this to ensure updated state -:
updateContactPath(path, index) {
const { contactPaths } = this.state;
const { setFieldValue } = this.props;
this.setState(
{
[...contactPaths, [index]: path],
},
() => setFieldValue('contactPaths', this.state.contactPaths),
);
}

how to render object array in react?

here is my componentDidmount
componentDidMount() {
for ( var i in course ) {
let title = course[i];
const ref = firestore.collection('courses');
ref.where("class", "array-contains", course[i]).get()
.then(querySnapshot => {
const count = querySnapshot.size
course_stats.push({
title: title,
value: count,
});
});
}
console.log(course_stats)
this.setState({
courses: course_stats,
})
}
here is my render
render() {
const { classes } = this.props;
if (this.state.courses) {
console.log(this.state.courses)
return (
<ul>
{course_stats.map(d => <li key={d.title}>{d.title}</li>)}
</ul>
)
}
on the line console.log, I can see the object array in that. However, when i try render that, it doesn't show anything.
this is the console.log capture
how can I render the title and value of array?
Thank you!
Adding to izb's answer, this.setState has already executed, so you should use async/await, or add a seperate callback function like this that returns a Promise
setAsync(state) {
return new Promise((resolve) => {
this.setState(state, resolve)
});
}
handleChange = (event) => {
return this.setAsync({[event.target.name]: event.target.value})
}

Why is setState is not working as expected

For some reason my setState isn't updating...My callback isn't even firing off. I tried defining the function onSubmit() as just onSubmit() instead of onSubmit = () =>. Any ideas? And yes I have verified that my if (milestoneBtnLabel === "Create") is executing.
constructor(props) {
super(props);
this.state = {
campus: []
};
this.onSubmit = this.onSubmit.bind(this);
}
onSubmit = e => {
const { milestoneBtnLabel, schoolData } = this.props;
e.preventDefault();
if (milestoneBtnLabel === "Create") {
this.setState(
{
campus: this.state.campus.concat(schoolData.schoolData.name)
},
() => {
console.log("here"); <-- Doesn't execute
this.props.saveChecklistItem({ ...this.state });
}
);
}
this.props.closeModal();
};
Maybe this.props.closeModal(); removes the component from DOM before setState() completes. Try moving that call to setState callback.
if (milestoneBtnLabel === "Create") {
this.setState(
{
campus: this.state.campus.concat(schoolData.schoolData.name)
},
() => {
console.log("here"); <-- Doesn't execute
this.props.saveChecklistItem({ ...this.state });
this.props.closeModal();
}
);
else {
this.props.closeModal();
}
There are couple of things in your code that needs correction
No need for manual binding when the function is declared as arrow function
Never recommend mutating an array using concate instead use previous state to push new values into array
Also you need to find a better way to close the model like making it wait for 2 seconds and then close the model. Because JavaScript execution will be in parellel so you need to make your dialog wait for previous actions to be completed. You need to think about closing the modal always whenever submit button is triggered but not just when it is only milestoneBtnLabel == "Create"
Change
onSubmit = e => {
const { milestoneBtnLabel, schoolData } = this.props;
e.preventDefault();
if (milestoneBtnLabel === "Create") {
this.setState(
{
campus: this.state.campus.concat(schoolData.schoolData.name)
},
() => {
console.log("here"); <-- Doesn't execute
this.props.saveChecklistItem({ ...this.state });
}
);
}
this.props.closeModal();
}
To
onSubmit = e => {
const { milestoneBtnLabel, schoolData, closeModal, saveChecklistItem} = this.props;
e.preventDefault();
if (milestoneBtnLabel === "Create" && schoolData && schoolData.schoolData){
this.setState( prevState => (
{
campus: [...prevState.campus, schoolData.schoolData.name]
}),
() => {
console.log("here"); <-- Doesn't execute
saveChecklistItem({ ...this.state });
}
);
}
setTimeout(()=>{
closeModal();
}, 2000);
}

Resources