I want to get the state inside of setTimeOut
change = () => {
setTimeout(function() {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
}) }, 3000);
};
in this this.state.categories i get the next error: "Cannot read property 'categories' of undefined"
That's because the callback you are providing to setTimeout is using the function keyword, which will have its own scope unless you bind it manually. You can fix that cleanly by making the callback an arrow function:
change = () => {
setTimeout(() => {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
})
}, 3000);
};
Based on your other comment, if you'd like to manually bind it instead, you can do this:
change = () => {
setTimeout(function () {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
})
}.bind(this), 3000);
};
Use arrow function, you missed the this binding. Arrow function will do this for you implicitly.
change = () => {
setTimeout(() => {
this.setState({
categories: [...this.state.categories, { time: 20, msg: "msg1", visible:true }]
}) }, 3000);
};
Related
I'm trying to update the uploadFiles state inside my updateFile function, when reloading the file, I'm rewriting this component in hooks, but inside the function the state is given as empty.
const [uploadedFiles, setUploadedFiles] = useState({
slides: [],
material: [],
});
const updateFile = useCallback(
(id, data) => {
const value = uploadedFiles.slides.map(uploadedFile => {
return id === uploadedFile.id
? { ...uploadedFile, ...data }
: uploadedFile;
});
console.log('value', value);
console.log('uploadedFilesOnFunction', uploadedFiles);
},
[uploadedFiles]
);
function processUpload(upFile, type) {
const data = new FormData();
data.append('file', upFile.file, upFile.name);
api
.post('dropbox', data, {
onUploadProgress: e => {
const progress = parseInt(Math.round((e.loaded * 100) / e.total), 10);
updateFile(upFile.id, {
progress,
});
},
})
.then(response => {
updateFile(upFile.id, {
uploaded: true,
id: response.data.id,
url: response.data.url,
type,
});
})
.catch(response => {
updateFile(upFile.id, {
error: true,
});
});
}
function handleUpload(files, type) {
const uploaded = files.map(file => ({
file,
id: uniqueId(),
name: file.name,
readableSize: filesize(file.size),
preview: URL.createObjectURL(file),
progress: 0,
uploaded: false,
error: false,
url: null,
type,
}));
setUploadedFiles({
slides: uploadedFiles.slides.concat(uploaded),
});
uploaded.forEach(e => processUpload(e, type));
}
console.log('slides', uploadedFiles);
I expected the state values to be viewed by the function. For me to manipulate and set the state.
There might be other issues, but one thing I've noticed is:
const [uploadedFiles, setUploadedFiles] = useState({
slides: [],
material: [],
});
// A setState CALL FROM THE useState HOOK REPLACES THE STATE WITH THE NEW VALUE
setUploadedFiles({
slides: uploadedFiles.slides.concat(uploaded),
});
From: https://reactjs.org/docs/hooks-state.html
State variables can hold objects and arrays just fine, so you can still group related data together. However, unlike this.setState in a class, updating a state variable always replaces it instead of merging it.
The setState from the useState hook doesn't merge the state. Because it can hold any type of value, not only objects, like we used to do with classes.
From your code you can see that you're erasing some property from state when you're updating like that.
Instead, you should use the functional form of the setState and access the current state prevState, like:
setUploadedFiles((prevState) => {
return({
...prevState,
slides: uploadedFiles.slides.concat(uploaded)
});
});
The updated updateFiles function:
const updateFile = (id, data) => {
setUploadedFiles(prevState => {
const newSlide = prevState.slides.map(slide => {
return id === slide.id ? { ...slide, ...data } : slide;
});
return {
...prevState,
slides: newSlide,
};
});
};
I getting typeerror: this.getISO is not a function.
I am trying to implement mapbox API with 3 isochrones plot on it. but i am getting an error that this.getISO is not a function.
map.on("load", function() {
map.addSource("iso", {
type: "geojson",
data: {
type: "FeatureCollection",
features: []
}
});
map.addLayer(
{
id: "isoLayer",
type: "fill",
// Use "iso" as the data source for this layer
source: "iso",
layout: {},
paint: {
// The fill color for the layer is set to a light purple
"fill-color": "#5a3fc0",
"fill-opacity": 0.3
}
},
"poi-label"
);
this.getISO(data => {
map.getSource("iso").setData(data);
});
});
this.map = map;
}
getISO(callback) {
const { duration, lng, lat } = this.state;
fetch(
`https://api.mapbox.com/isochrone/v1/mapbox/driving/${lng},${lat}?
contours_minutes=${duration}&polygons=true&
access_token=pk.eyJ1Ijoiam9uYnN0b3JleSIsImEiOiJjanl5aGFyaWwxaGE3M21ycnhsNGpvYmk2In0.jfxHlg5boDqdiUgf3cco2A`
)
.then(response => response.json())
.then(data => {
callback(data);
});
}
in first-line replace function definition with arrow function and the
like this map.on("load", ()=> {
Use an arrow function () => { instead of function() {.
I have a print function, it first sets the state of isPrinting to true and open a pring dialog. Once the dialog is being closed it sets the state of isPrinting to false and at this point I'm getting the error (second setState):
Uncaught ReferenceError: setState is not defined
I binded function to current context with the arrow function.
handlePrint = () => {
this.setState({ isPrinting: true }, () => { //setState is working working properly
window.print();
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener = (mql) => {
if (!mql.matches) {
this.setState({ isPrinting: false }); //Error
}
}
}
});
};
I am not sure what you are trying to achieve here but the window.print() freezes the app. There is no code being run unless someone clicks the printing screen away. I works just like window.alert("..."). You can try that by printing a thimestamp right after the win.print. So besides that there is a problem with the this context that cannot be reached the whole function is useless. Because you could just do:
handlePrint = () => {
this.setState({ isPrinting: true }, () => {
window.print() //freezes until someone clicks it away.
this.setState({ isPrinting: false }) //Error
})
}
Regards
Try this.
handlePrint = () => {
let _this = this;
this.setState({ isPrinting: true }, () => {
window.print();
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener = (mql) => {
if (!mql.matches) {
_this.setState({ isPrinting: false });
}
}
}
});
};
This should help
handlePrint = () => {
this.setState({ isPrinting: true }, () => { //setState is working working properly
window.print();
if (window.matchMedia) {
var mediaQueryList = window.matchMedia('print');
mediaQueryList.addListener = (mql) => {
if (!mql.matches) {
return { isPrinting: false };
}
}
}
});
};
setState method should return a new state rather than try to execute anything.
At the second time, instead of setState, just return the new state like:
return {
isPrinting: false,
};
How did you use the function 'mediaQueryList.addListener'?You can console the two 'this' and see if they are same.
Below is a function getting messages from firebase database, but it only setState to only one message.
However, the console.log can log multiple messages in the object.
Is there anything wrong in my function?
getMessages(){
var messages = [];
firebaseApp.database().ref('users/'+firebase.auth().currentUser.uid+'/userChat/'+firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
//alert(JSON.stringify(dataSnapshot.val()));
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message)=>{
//alert(JSON.stringify(messages));
messages.push({
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
});
this.setState({
messages: messages
});
console.log('woooo'+JSON.stringify(messages));
});
});
});
}
You are setting the state inside the forEach Block. try moving it outside the iteration block
As JanneKlouman mentioned it's not good enough to remove it from the iterration block as you are doing async calls.
You can create a new array and set it in the state on each iteration, react will batch those set state calls:
function getMessages() {
var messages = [];
firebaseApp.database().ref('users/' + firebase.auth().currentUser.uid + '/userChat/' + firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
//alert(JSON.stringify(dataSnapshot.val()));
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message) => {
const newMessage = {
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
}
const nextState = this.state.messages.map(message => {
return {
...message,
user: {...meesage.user} // i think we must do this in order to break out of the reference as spreading will only work on a shallow level
}
});
this.setState({
messages: [...nextState, newMessage]
});
});
});
});
}
Try cloning the array before setting state:
getMessages(){
let messages = [];
firebaseApp.database().ref('users/'+firebase.auth().currentUser.uid+'/userChat/'+firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message)=>{
const message = {
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
};
// Clone messages
messages = [...messages, message];
this.setState({ messages });
});
});
});
}
Function to update state:
animate() {
setInterval(function(){
setTimeout( this.setState({
a: '123'
}), 1000);
}, 4000);
}
The method called:
componentDidMount() {
this.animate();
}
Error:
Uncaught TypeError: this.setState is not a function
Then the next code tried:
animate() {
setInterval(function(){
setTimeout( () => {this.setState({
a: '123'
})}, 1000);
}, 4000);
}
And the next error was:
Uncaught TypeError: _this2.setState is not a function
The problem stems from your definition of setInterval.
When you do setInterval(function() {...}), the this keyword is no longer bound to the React component but to the function it is called within because of the introduction of the new scope.
You can either switch it to:
animate() {
const self = this
setInterval(function() {
setTimeout(() => self.setState({ a: '123' }), 1000)
}, 4000)
}
This way self is assign to the React component value of this before the new scope is introduced, or you can use all arrow functions to preserve the this keyword referencing the component:
animate() {
setInterval(() => {
setTimeout(() => this.setState({ a: '123' }), 1000)
}, 4000)
}
Although the answer submitted by m_callens is the proper one, I'll add this possibility using bind as well, for those who use pre-ES6 JavaScript.
animate() {
setInterval((function(){
setTimeout((function() {this.setState({
a: '123'
})}).bind(this), 1000);
}).bind(this), 4000);
}