This may be a bug.
In my simple react-app, inside a component's componentDidMount()
I have a
document.addEventListener('message', function(e) {//callback})
However, the callback of this event listener DOES NOT update the component even after calling
this.setState{states:states}
inside it
I can confirm indeed that the callback is fired, but it does not update the component.
Is this a bug? What would be a work around for this?
Hi, I don't see any problem, could you give example.
Or maybe you should check context of function call ?
https://jsfiddle.net/taqhr2bL/1/
Hi, this shouldn't work since in addEventListener the context for this keyword is bound to the document. You should receive an error. The way around this is 1. use arrow function for your callback, which bind this automatically. 2. use bind() to bind the value of this keyword.
@OlegLustenko and @pavelsuraba are correct. The context here:
document.addEventListener('message', function(e) {
// this is `document`. Calling `this.setState` will raise an exception
})
Will be the document. The options they suggest are:
Use an arrow function. This is preserves the scope to that of the parent closure - the component:
document.addEventListener('message', (e) => {
this.setState({ stuff: true })
})
Use Function.prototype.bind. This returns a copy of a function with a specific context:
document.addEventListener('message', function (e) {
this.setState({ stuff: true })
}.bind(this)) // this is the component
Hope it helps!
If you have es6 at your disposal, use bind
document.addEventListener('mousedown', this.onMouseDown.bind(this));
When onMouseDown
runs it will no longer be bound to #document
but to the this
that set the event listener
@nhunzaker How do I do that with Hooks?
@Tarrasque18 It'll depend on what you need to do, but I think you could do that like:
import React, { useEffect } from 'react'
function MessageComponent() {
useEffect(() => {
const onMessage = event => {
// handle event
}
document.addEventListener('message', onMessage)
return () => {
document.removeEventListener('message', onMessage)
}
}, [])
// return JSX
}
@Tarrasque18 It'll depend on what you need to do, but I think you could do that like:
import React, { useEffect } from 'react' function MessageComponent() { useEffect(() => { const onMessage = event => { // handle event } document.addEventListener('message', onMessage) return () => { document.removeEventListener('message', onMessage) } }, []) // return JSX }
If you do this, you need to pass also your hook like :
import React, { useEffect, useState } from 'react'
function MessageComponent() {
const [loading, setLoading] = useState(false)
useEffect(() => {
const onMessage = event => {
// handle event
}
document.addEventListener('message', onMessage)
return () => {
document.removeEventListener('message', onMessage)
}
}, [loading, setLoading])
// return JSX
}
this will work but , if you are rendering something by setting some same here , than it will go slow after two or three state changes
any idea why ! how to reduce the render
Most helpful comment
@OlegLustenko and @pavelsuraba are correct. The context here:
Will be the document. The options they suggest are:
Use an arrow function. This is preserves the scope to that of the parent closure - the component:
Use Function.prototype.bind. This returns a copy of a function with a specific context:
Hope it helps!