I'm a facing a bit of a problem here. I'm trying to pass a parameter from a function to this.setState callback, but I can't figure out how is this possible.
My code looks like this:
selectHandler(event){
this.setState({
selectedImage: event.target
}, (event) => {
this.markSelectedHandler(event)
})
}
markSelectedHandler(e){
e.target.classList.add('active')
if(e.target !== this.state.selectedImage && this.state.selectedImage){
this.state.selectedImage.classList.remove('active')
e.target.classList.add('active')
}
}
e.target returns null, any idea why this happens ?
The event will not work async. You will need to extract the values or use e.persist() reactjs.org/docs/events.html#event-pooling
You could however say:
selectHandler(event){
const { target } = event;
this.setState({
selectedImage: target
}, () => {
this.markSelectedHandler(target)
})
}
markSelectedHandler(target){
target.classList.add('active')
if(target!== this.state.selectedImage && this.state.selectedImage){
this.state.selectedImage.classList.remove('active')
target.classList.add('active')
}
}
But I will recommend against it..
To be honest, you should not add your class with DOM manipulating but instead add it in your render <img className={this.state.selectedImage === myImage ? 'active' : undefined} />
You are shadowing your event in this code:
selectHandler(event){
this.setState({
selectedImage: event.target
}, (event) => {
this.markSelectedHandler(event)
})
}
You need not to shadow, by not passing a parameter with the same name (event to the setState callback):
selectHandler(event){
this.setState({
selectedImage: event.target
}, () => {
this.markSelectedHandler(event)
})
}
Try not sending event as an argument to the callback
eg you have written
selectHandler(event){
this.setState({
selectedImage: event.target
}, (event) => {
this.markSelectedHandler(event)
})
}
Write like this instead
selectHandler(event){
this.setState({
selectedImage: event.target
}, () => {
this.markSelectedHandler(event)
})
}
Related
In my axis, I am trying to dispatch a React event with a payload. When I console.log('item'), I get a pointer event logged. The dispatch has as payload: isTrusted (from the pointer event) instead of having the item property dispatched. Is this the correct way to trigger a dispatch event with D3.js?
axisGroup
.selectAll('.tick')
.data<BaseItemI>(itemsInDomain)
.style('cursor', 'pointer')
.on('click', function (item) {
console.log('clicked', item);
// dispatch.arguments = { type: SET_SELECTED_ITEM, payload: item };
dispatch({
type: SET_SELECTED_ITEM,
payload: { item: item, baseItem: undefined }
});
});
I tried using the dispatch from D3 but it is confusing. Also tried using a callback function instead of an anonymous function and I get the same behaviour.
.on('click', (item) => {
console.log('clicked', item);
dispatch({
type: SET_SELECTED_ITEM,
payload: { item: item, baseItem: undefined }
});
});
I did not manage to get the .on('click', () => {}) to work but I instead added an onClick to the g element that contains the axisRef.
<g
ref={ref}
style={{ cursor: 'pointer' }}
onClick={() => {
itemsInDomain.map((i) => {
dispatch({
type: SET_SELECTED_ITEM,
payload: { item: i, baseItem: undefined }
});
});
}}
></g>
i am using v-click-outside and want to pass value to the function but i am having error
This is how i am passing a value
Here is my code from Main.js file
vue_app.directive('click-outside', {
beforeMount(el, binding, vnode) {
el.clickOutsideEvent = evt => {
evt.stopPropagation()
if (!(el === evt.target || el.contains(evt.target))) {
binding.value(evt, el)
}
}
// Wait 1 frame otherwise a potential click that mounted the element will immediately trigger a click-outside event:
window.requestAnimationFrame(() => {
document.addEventListener('click', el.clickOutsideEvent)
})
},
unmounted(el) {
document.removeEventListener('click', el.clickOutsideEvent)
},
});
You have to pass a function to the directive, but right now you call it right away and pass the returned value. So instead of
v-click-outside="hideAllShowDetailDropdown(show.show_ID)"
Try this:
v-click-outside="() => hideAllShowDetailDropdown(show.show_ID)"
I'm having lots of elements on which #mouseenter set a value to true and #mouseleave sets it to false. Basically what I need is a way to set a reactive variable to true if the mouse hovers the element.
I've been trying to figure out how to write such custom directive from the docs but it only mentions how to use .focus() js function on an element. Which js functions would be used for said directive?
Something like:
const vHover = {
mounted: (el) => {
el.addEventListener('mouseenter', state.hover=true)
el.addEventListener('mouseleave', state.hover=false)
}
}
I think you could do something like:
app.directive('hover', {
created(el, binding) {
const callback = binding.value
el.onmouseenter = () => callback(true)
el.onmouseleave = () => callback(false)
},
unmounted(el) {
el.onmouseenter = null
el.onmouseleave = null
}
})
Template:
<button v-hover="onHoverChange">Example</button>
Methods:
onHoverChange(isHovered) {
console.log(isHovered)
}
I believe this is not the intended use of directives. The value of the state cannot be mutated within the directive. You can pass the variable through the binding, but you cannot update it.
binding: an object containing the following properties.
value: The value passed to the directive. For example in v-my-directive="1 + 1", the value would be 2.
oldValue: The previous value, only available in beforeUpdate and updated. It is available whether or not the value has changed.
so if you do el.addEventListener('mouseenter', binding.hover=true), as you may have noticed, it will not update the state.
However, if we use the internals (PSA: though not recommended since they could potentially change at any time), you could get instance using the vnode, and use the binding.arg to denote which Proxy (state)
so you could get the reactive variable with vnode.el.__vueParentComponent.data[binding.arg]
<script>
export default {
data(){
return {
state: { hover:false }
}
},
directives: {
hover: {
mounted(el, binding, vnode) {
el.addEventListener('mouseenter', () => {
vnode.el.__vueParentComponent.data[binding.arg].hover = true
})
el.addEventListener('mouseleave', () => {
vnode.el.__vueParentComponent.data[binding.arg].hover = false
})
},
}
}
}
</script>
<template>
<h1 v-hover:state="state">HOVER {{ state }}</h1>
</template>
SFC playground link
of course you might want to add the unmounted and even consider adding mouseleave dynamically only when mouseenter fires
This is how it can be done inside the component:
const vHover = {
mounted: (el) => {
el.addEventListener('mouseenter', () => {state.hover=true})
el.addEventListener('mouseleave', () => {state.hover=false})
},
unmount: (el) => {
el.removeEventListener('mouseenter', () => {state.hover=true})
el.removeEventListener('mouseleave', () => {state.hover=false})
}
}
I need to activate enter button after captcha verified but currently it's not working. It shows anchor:1 Uncaught (in promise) null below is my whole code. I'm using https://www.npmjs.com/package/react-google-recaptcha
enterkey = event => {
console.log(event)
if (event.key === "Enter") {
this.submitHandler(event);
}
};
captchaValidate = (value) => {
this.enterkey();
console.log(value)
if (value) {
this.setState({ disabled: false })
} else {
this.setState({ disabled: true })
}
}
<ReCAPTCHA
sitekey={captcha_key}
onChange={this.captchaValidate}
className="g_capctha"
/>
I think problem is with event because event is getting undefined when captchaValidate called. How can I get event in this captchaValidate because changing value to event it gives only some text similar like any token. Please help.
you can make it works by making an async promise-based value fetching.
captchaValidate = async (value) => {
this.enterkey();
console.log(value)
if (value) {
this.setState({ disabled: false })
} else {
this.setState({ disabled: true })
}
}
I need to use navigator.sendBeacon() on window unload in order to let my server know the client has closed his window. I have searched everywhere and it just doesn't work for me.
For reference, the solution in this post didn't work either.
I have an App component that wraps my entire project. I am trying to set the unload event on it's componentDidMount() lifecycle method, and it just won't fire.
componentDidMount() {
window.addEventListener("beforeunload", this.unload);
}
componentWillUnmount() {
window.addEventListener("beforeunload", this.unload);
}
unload(e) {
e.preventDefault();
e.returnValue = 'test';
navigator.sendBeacon(`http://localhost:8080/window-closed/${this.props.username}`);
return 'test';
}
I expect the server to get the AJAX call, and the window to prompt the user 'test' before the window is closed. What actually happens is the window just closes as usual.
NOTE: the return 'test' & e.returnValue = '' statements are purely for testing. I'm only interested in the AJAX request.
Any help would be much appreciated.
If you're using a functional component, you can try this:
useEffect(() => {
window.addEventListener("beforeunload", handleUnload);
return () => {
window.removeEventListener("beforeunload", handleUnload);
};
}, []);
const handleUnload = (e) => {
const message = "o/";
(e || window.event).returnValue = message; //Gecko + IE
return message;
};
You should bind this to the unload method or transform it to arrow function.
Binging way
constructor() {
super();
this.state = {
//stuff
};
this.unload.bind(this);
}
componentDidMount() {
window.addEventListener("beforeunload", this.unload);
}
componentWillUnmount() {
window.removeEventListener("beforeunload", this.unload);
}
unload(e) {
navigator.sendBeacon(`http://localhost:8080/window-closed/${this.props.username}`);
}
Arrow functions way:
constructor() {
super();
this.state = {
//stuff
};
}
componentDidMount() {
window.addEventListener("beforeunload", this.unload);
}
componentWillUnmount() {
window.removeEventListener("beforeunload", this.unload);
}
unload = (e) => {
navigator.sendBeacon(`http://localhost:8080/window-closed/${this.props.username}`);
}
Remember to remove the eventlistener on componentWillUnmount (you are currently adding it again).
You may be able to use navigator.sendBeacon.
const UnloadBeacon = ({
url,
payload = () => {},
children
}) => {
const eventHandler = () => navigator.sendBeacon(url, payload())
useEffect(() => {
window.addEventListener('unload', eventHandler, true)
return () => {
window.removeEventListener('unload', eventHandler, true)
}
}, [])
return children
}
full example here: https://gist.github.com/tmarshall/b5433a2c2acd5dbfc592bbc4dd4c519c
Have you tried declaring upload function as a fat arrow function? Also declare it before componentDidMount. (for better readability) before passing it as a reference.
Also have you tried attaching listener in contructor ? And make surw to bind your function in constructor. For reference
Also destroy the listener at componentWillUnmount, instead of adding it. (useless) use reference to listener, to destroy. Which you will create in constructor.
Best of luck
I am unsure why beforeunload is not working, but as a workaround, you may consider using the hashchange event.
componentDidMount() {
window.addEventListener("hashchange", this.doSomething, false);
}
componentWillUnmount() {
window.removeEventListener("hashchange", this.doSomething, false);
}