In my app, I wish to not use React's own event system. As per the docs:
If you find that you need the underlying browser event for some reason, simply use the nativeEvent attribute to get it.
However, the following doesn't work and returns an error.
// onclick is DOM native event-handler; React's alternative is onClick
<div id = "main" onclick = {( e ) => this.onclickHandler( e )}...
// console error: Unknown event handler property onclick...
Edit:
I understand I can do something along the lines of the following, however, I wonder why the above wouldn't work. Is the following way of binding the handler via addEventListener the only way?
componentDidMount(){
window.addEventListener("click", this.handleblahblah);
...
}
handleblahblah(){
...
}
After thinking about it, I have come to realise my mistake. In case someone else wonders about this, here is the reason this above binding of onclick doesnt work.
React Components are not the same as DOM elements.
In the above code, we are delegating binding of methods over to React. React doesn't recognise onclick, which is a DOM keyword for handling click events.
An alternative to above solution (using addEventListener) could be to access the element via React's refs method. refs gives you access to the underlying DOM element. Then bind onclick method.
class exampleComponent extends Component{
...
componentDidMount(){
this.mainInterface.onclick = (e) => this.onclickEventHandler(e);
}
render(){
...
<div id = "main" refs = {( main ) => this.mainInterface = main }
...
Related
I'm curious, what's the best way to forward or dispatch events across multiple levels in component tree in Svelte JS?
Say I have App.Svelte, some intermediate number levels, each containing a child component, and Modal.Svelte. If I want to forward or dispatch an event from Modal to App, what's the right way to do this?
As I understand it, event forwarding in Svelte will traverse up the component tree and forward the event to the first parent that references the forwarded event. (Is this the correct interpretation?)
And using event dispatch approach, each nested component would need to 1/ import createEventDispatcher, 2/ create a dispatcher variable, 3/ define a function, which dispatches the event. Then parent's would need to import the function and reference it inside a tag, such as <p>. (Is this correct?)
If I'm correct on both of the above, I'm wondering if there isn't a more streamlined approach, eg connecting the event to stores, which would effectively flatten the component tree such that any component could receive the forwarded event. Though I imagine that this could induce some hard to debug behavior if multiple components reference the same forwarded event.
To forward a event from a child component or DOM element to the parent, you just have to define an on:event without handler. E.g.
<button on:click >
<Component on:open >
No need to use createEventDispatcher just for forwarding.
If you want to share events more widely, you can create an EventTarget and send events through that. Since subscriptions would not happen in the template via on:event, you have to make sure to remove listeners again. You can also use a context to just expose the object to a sub-tree of the component hierarchy.
Alternatively, events can be dispatched directly to DOM elements, such events will bubble up the entire element hierarchy (if enabled in the event options).
someElement.dispatchEvent(
new CustomEvent('my-event', { bubbles: true })
);
If you dispatch events to the window, directly or via bubbling, you can subscribe to those more conveniently using svelte:window:
<svelte:window on:my-event={() => ...} />
(These handlers are removed automatically when the component is destroyed.)
You might consider instead using accessor functions. In App.svelte, define a function that manipulates your top-level variables. Then place that function in an accessor object and pass that down as a prop to all your components that may need it.
<script>
[App.svelte]
let myVar, myObj, myWhatever
function updateMyVal(newValue) {
myVar = newValue
}
function mergeMyObject(mergeObj) {
myObj = {...myObj, ...mergeObj}
}
let accessorObject = {
updateMyVal: updateMyVal,
mergeMyObject: mergeMyObject
}
</script>
<ChildComponent {accessorObject} {myVar} {myObj} />
. . .
[ChildComponent.svelte]
<script>
export let accessorObject, myVar, myObj
accessorObject.updateMyVal(1234)
accessorObject.mergeMyObject({newProp: newVal})
</script>
And so forth... this has the advantage of pushing changes to application-wide variables from the top down, which I've found to work better for complex SPAs than a web of events, two-way-bindings or stores, at least in my limited experience.
A question like this:
How is it possible in vue3 after an event to call a method in another component?
Found this solution for vue2
this.$refs.myComponent.myMethod()
But how is it supposed to work without "this" in vue3 + composition api?
If this is not possible then what are the alternatives?
The event itself occurs in the following code:
(for example, we can take the resize event of a component and set a task - after it, activate a method inside another component)
<Vue3DraggableResizable class="chartApp"
#resizing="print('resizing')"
>
How is it possible to implement it?
Not sure about if they are best practices but here we go.
I think you can watch your props change and trigger an event like this
import { watch, toRefs } from 'vue'
const props = defineProps({
yourReactiveProp: Boolean
})
const { yourReactiveProp } = toRefs(props) // this is reactive
watch(
() => yourReactivePropn.value,
(newVal) => {
yourMethod() // you can pass the new value also.
})
You can use mitt package. Literally, you can listen and fire emit from anywhere to anywhere. I.e, parent-child, child-parent, siblings, non-siblings, etc..
I would like to llisten to an event that I can see in Chrome DevTools Vue, but which I do not know how to address.
For a Root event I use
this.$root.$on("note_id", (note_id) => (this.note_id = note_id));
How should I address an event that comes from a specific component? In the example below, I would like to act upon the hide event:
To take the $root example below, I would like to use
this.<here I do not know what to use>.$on("hide", () => this.someVariable = true);
Add a reference to the component (<your-component ref="yourComponent" />) and then access its events via
this.$refs.yourComponent.$on(...)?
In react, when you have an element with an onClick prop, it's easy to use Enzyme's .simulate("click") method to click it. However, there's an example in my codebase where an element is referenced by React's "ref" prop, and then that element's .addEventListener is invoked to bind an event handler to it.
I've provided a code example:https://codesandbox.io/s/1z4xok048j
The key line is this:
if (this.refs.hey) {
this.refs.hey.addEventListener("click", this.handleClick);
}
In the code example, the eventHandler is not bound until componentDidUpdate is run, so if you click on the "click me to increment counter" div, the child element receives new props and its componentDidUpdate triggers. Then if you click on the Child element, its eventHandler triggers as expected. However, I can't reproduce this behavior in my Enzyme/Jest tests.
I've done the obvious .simulate("click") method from Enzyme and it does not work; when I change from using refs and the event listeners to using an onClick, .simulate("click") works as expected. However, removing the refs causes other bugs to surface and I haven't been able to figure out why, so I'd prefer to find out a way to simulate clicking the button.
The code for .simulate("click") actually looks for the onClick function and passes the params to it, there is no 'actual' click going on. You might have to mock the addEventListener functions with something like
// Set-up event listener mock
const map = {};
window.addEventListener = jest.genMockFn().mockImpl((event, callback) => {
map[event] = callback;
});
The answer is actually really simple. The general idea is to .find the node and get the underlying HTML DOM Node with .getDOMNode(). Once you have it, replace its .addEventListener like so:
const map ={};
const namedByRefComponent = component.find(".some-class-name");
const namedByRefDomNode = namedByRefComponent.getDOMNode();
namedByRefDomNode.addEventListener = jest.fn().mockImplementation((event, cb) => {
map[event] = cb;
});
after that, your DOM node's event handlers can be found in map and you can invoke them in the tests.
I have a react-redux app which requires a part which is conditional.
i.e. it can either be a div with a certain className or a div with the same className and an onClick handler which fires an action. so I created a function which returns jsx according to the above conditions.
Now the problem is that the onClick is not being added to the props as I expected but className is working fine.
class someClass {
renderSomething(){
return <div className="someClass" onClick={() =>
this.props.someAction()}>Something</div>
}
render(){
{this.renderSomething()}
}
}
What I expected it to be
<div className="someclass" onClick={() => this.props.someAction()}>Something</div>
What Rect dev tools show
<div className="someclass">Something</div>
Don't know where I went wrong.
Edit 1: The function was mistakenly written outside the class.
You have a typo. In your renderSomething() method, you should have the following:
renderSomething() {
return (
<div className="someclass" onClick={this.props.someAction}>Something</div>
);
}
EDIT: Now I saw that renderSomething was not a part of class from it was called from. In order to keep reference to this, you have to move it inside of your class, hence make it class method.
If you wish to add an argument to someAction function, I suggest you add a event handler to your React component, and then assign it to a onClick. Using arrow function in render method causes creation of a new function in memory every time component re-renders.
Maybe you meant to do this?
class someClass {
renderSomething(){
return <div className="someClass" onClick={() => this.props.someAction()}>Something</div>
}
render(){
{this.renderSomething()}
}
}
In your original example, renderSomething wasn't a part of someClass -- so calling this.renderSomething() would have thrown a runtime error.
I could be wrong, but i think
render(){
return {this.renderSomething()}
}
Is the way to go. Let me know if render(){this.renderSomething()} works.