ReactJS "eating" my onClick attribute - javascript

I'm doing a ReactJS web app, and everything seems to be doing good... except for one silly thing that I can't figure out why is happening.
I'll try to reduce to the minimum implementation necessary.
I've an Item component: http://pastebin.com/P8T6vAhR
A container, which renders the List and defines the function properly: http://pastebin.com/GmyrnY2c
And a List component which renders each of the items and pass the function as a parameter (after receives it from the Container): http://pastebin.com/6cwPCJhn
The function seems to be fine, and I can see that the function is there when I ask for log it on the console.
But then what I got rendered instead is:
<button class="ui">My Category</button>
Hey, where's the onClick attribute? It's gone... no idea why.
Probably is some stupid mistake or misunderstanding about the magic behind JSX, but I can't find the problem... specially because I use similar things on other parts of my code, and everything else is working nicely.
I appreciate any help.

Hey, where's the onClick attribute
The attribute is just converted to a property that gets passed to the component.
The dom onclick is not the same as the component prop (react does fancy event delegation which performs better and doesn't suffer from onclick global scope).

Related

How to dynamically access children of a slot

Let's say I have a SearchForm Component that has a Reset button, as well as a slot to include any desired SearchField Components. When I click SearchForm's Reset button, I'd like to call each SearchField's reset method, but I'm having a hard time understanding how to do this dynamically... I obviously don't want to add refs to each SearchField because these aren't static and can change when using the SearchForm in some other part of the application. Fiddle for example.
In Vue2, it seemed liked there was some sort of $children property, but that was taken out in Vue3. I was thinking I could potentially use querySelectorAll to access all "input" elements, but I didn't see how I could access the DOM element's component instance (similar to jQuery's $ selector). If I access the $slots.default() and loop over it, I get some weird object that isn't a component instance... or rather, it doesn't have the typical properties that the component instance has, and I have no clue how to access the actual instance from here.
It's also possible I'm not thinking in a Vue-centric way, as I'm new to the framework, so how can I solve this issue?
I've come up with this solution, but I don't like it, as it adds some minor coupling. Basically, I listen for when the field is created, check its parent, and if it's a form, I push it onto the array of children. Then when the parent's reset is called, it loops through its children. This is a fragile approach because it requires the direct parent to be the form... if the field was nested inside of another component, it won't be added to the form's fields. I'm also pretty sure this breaks the best practices of the framework. It's a shame there doesn't appear to be a way of accessing child instances (without being forced to use ref)... that seems like it'd be desired by a lot of devs.
I came up with yet another way, but once again, seems a little shady because I'm accessing the DOM element's private property __vueParentComponent. I like it better than the previous answer because it's not coupled, and I can use getElementsByTagName. Fiddle for reference. This is the relevant code that I added as a method in SearchForm:
getFields() {
const fields = this.$el.getElementsByTagName("input");
// getElementsByTagName returns an HTMLCollection, which doesn't have map,
// so let's use spread to make an array and use map
return [...fields].map((fieldEl) => {
return fieldEl.__vueParentComponent.proxy;
});
}
Last solution... this one seems to be more stable and the proper Vue way. You use provide/inject; the parent provides the value, and the child injects it, so it can use it. Vuetify does something similar, but they have their own register and unregister methods, which I've created in the Fiddle but as a rudimentary implementation. The only caveat being if you're using TypeScript, the inject won't work properly, and you'll have to use one of these solutions.

What constitutes an appropriate use of ref in React

Can someone explain how ref is used in React? I understand it's a shortcut that sort of defeats the purpose of the react DOM, but I don't know how or why exactly. I'm trying to determine whether something I'm trying to accomplish represents one of the rare cases where you should use ref
I want a custom bootstrap alert I can show from any of these pages, without using JQuery (I have one working with JQuery already)
I have a router that switches between pages, each containing a Layout component with a Page component inside (based on this)
Like:
render() {
return (<Layout ref={layout => (this.layout = layout)}>
<WhateverPage
session={this.session}
otherExampleProp={"something"}
showAlert={this.showAlert}/>
</Layout>);
}
showAlert(type, text, hasTimeout, timeoutMs) {
this.layout.alert.showAlert(type, text, hasTimeout, timeoutMs);
}
I can think of three solutions:
Use ref, which as I only partially understand defeats the purpose of react components to some extent, but I'm not sure how exactly...
Use ref, but to a lesser extent, by placing the alert component in each Layout before the Page component (so no need for a ref to ).
Create a component and a function on each page, using the page's state to control the alert, so it would be basically the same as creating a unique alert for each page, which also defeats the purpose of a component...
The example most people give when explaining what to use ref for involves focus() - is this similar? Intuitively it feels like I should use ref, but I also know that theoretically you shouldn't, but I want to understand why, because there are exceptions and for all I know this may count.
Similarly, I want to create a confirm component to replace the native JS confirm() (since it might be deprecated soon), and this approach (using ref) also makes this WAY easier than creating a component for each page, since I can pass any function as a parameter to the confirm component for it to execute on an OK button press (also gives me the option of including icons, titles, custom buttons, etc).
Existing examples and libraries all seem to use method 3 (or they're simpler, and not not really analogous).
Is ref ok to use here? Is it wrong? Why? Am I overthinking this?
Yes, you are "misusing" ref here because you're trying to build around how React is intended to be used.
ref is mainly for accessing the actual rendered DOM element - maybe to focus it, read input, get dimensions, whatever. Generally speaking though you should us ref as a "read only" feature - use it to get info about the rendered DOM but don't use it as part of a process to bypass render() or inject elements into the DOM.
What you should do is create a reusable component for your Alert. Make it flexible enough that it can accept arbitrary settings like color, text, duration, callback functions for accept/cancel/clear, etc. Then you can just render it somewhere, maybe like this:
<MyAlert
title="foo"
text="bar"
duration={5}
confirmCallback={someFunction}
cancelCallback={anotherFunction}
/>
Remember that components are a way to render and interact with state, and that's exactly what you're trying to do with your Alert. There is some kind of notification, it has content and controls for doing some action(s), and all of that should live somewhere in your app state hierarchy. There is absolutely no reason to resort to refin this situation.
To complement the jered answer:
If you plan to use the alert component in all the pages then instead of placing an alert component inside each page you can create a high order component named like PageWithAlert (I would say just Page) that includes the alert component for each page.
You might want to take a look at this https://facebook.github.io/react/docs/higher-order-components.html

Polymer 1.0 Lifecycle Event for Complete Rendered DOM

Background
I've been working with Polymer for a while. I've been converting from .5 and building new elements for a production app. We are currently using Polymer 1.0.6, and this particular issue is also using jQuery 2.x.x and typeahead.js.
Issue
We have an element that builds a dynamic list of label and inputs provided by a data source. in the ready function we get a list of input data, and set that to a local list variable that is bound to a foreach template to create the labels and inputs.
I was unable to find a Polymer element I really liked for typeahead, for Polymer 1.0. So I defaulted to using typeahead.js. my problem is that I cannot find a lifecycle event or workaround, to call the typeahead function after the dom has processed setting the bound list in the ready function.
Code
The easiest way to demonstrate this issue, was to create a HEAVILY trimmed down version in a jsbin. I know the element looks bad, it was cut down as much as possible to demo the core issue I'm facing.
http://jsbin.com/zivano/edit?html,output
What Have I Tried?
I've tried using the attached event, and while it does process after the ready function, the dom changes from ready have not taken effect. I found similar issues on SO domReady vs ready - Migrating to Polymer 1.0 I've tried both suggestions, the second is still being used in the jsbin, without success.
I have also bound the click event of my inputs to a function calling the typeahead setup code, to prove that if the calls are made after the dom is rendered it will work correctly.
Summary
If update a data bound, local variable in the ready function, is there a lifecycle event I can call that will guarantee that those dom changes will be rendered, so I can make a dom query against those new items? Or is there a work around that will let me call a js function on a dom element, one time after the element dom fully renders?
my problem is that I cannot find a lifecycle event or workaround, to
call the typeahead function after the dom has processed setting the
bound list in the ready function.
I think I had a problem like this . For my problem I found a solution using the following :
var self = this;
window.addEventListener('WebComponentsReady', function(e) {
// imports are loaded and elements have been registered
/*Example*/
console.log('Components are ready');
var p = self.getElementsByTagName("paper-item");//paper-item created dynamically
console.log(p);//can access and use this paper-item
/*Finish example*/
//here you can call typeahead because the dom has been processed
});
Sorry for my English or if I dont understand your question, my English is bad.
The Issue I had was that the data-bound list was populated through an ajax function, which was completed after the attached function, even if I made an async call inside of the attached function, it would still fail because of race conditions.
It's worth noting the answer by Flavio Ochoa, will work. I personally preffered to not have my custom elements add listeners to the Window. So i went a different route.
Since my issues we're predicated on guaranteeing that the bound list was updated, I wrapped the ajax call in a Promise, and added the typeahead init logic to the then statement. That solution appears to be working.
I do have some concerns whether the promise can guarantee that the bound list will have propagated to the DOM by the time the then statement is processed. But so far it has worked consistently. I'll edit this answer if I can prove otherwise.

React.js: Why is there no componentDidRender event?

I have just started using React, and a couple of times I have thought to myself: "Why is there no componentDidRender event?".
Say that I have a component that renders a table to the DOM, and I want to use bootstrap-sortable on this table to allow the user to sort on whatever column he wants. In the case of bootstrap-sortable you need to run $.boostrapSortable() after the table is drawn, in order to initialize the plugin.
As I see it, there are two handlers on a React component that would be logical to consider to use for this purpose:
componentDidMount: This does not work because the DOM does not seem to be updated at this point of the execution.
componentDidUpdate: This could possibly work, but it does not fire on the initial render.
I am not saying that React is actually missing a componentDidRender function, because I assume that there is a perfectly logical explanation as to why it is not there. I am just asking if someone could explain why such a function is not present, and what would be the "React way" to handle a case like the one above.
In componentDidMount you can do: this.getDOMNode() to get a reference to the underlying DOM for that component. So if you do want to use your mounted component with jQuery you can do:
componentDidMount: function() {
$(this.getDOMNode());
}
http://facebook.github.io/react/docs/working-with-the-browser.html
Here's a fiddle which shows jQuery acting on the DOM node of a react component:
http://jsfiddle.net/sa5e88ys/1/
As you can see, it adds a border to the div as expected. If you're still having problems I guess it could be with the plugin you're using rather than jQuery or react?
Although there's no componentDidRender, you can make a method with the desired behavior and call it in both componentDidMount (which is only called after the first render) and componentDidUpdate (which is called after every render but the first).
Also, this is the preferred way of getting a ref to a DOM node from within the component:
https://facebook.github.io/react/docs/refs-and-the-dom.html

Reactjs component.setProps() alternative, how to send variable to detached element

I am using things like:
var MUSIC = React.renderComponent( Music({ }), document.getElementById("music-div"))
to later in the script, in an independent element (so not parent of MUSIC) do:
MUSIC.setProps({ url: 'http://...' })
to send a song to de music player, which is detached from the rest, so it does not accidentally gets refreshed by react, because it was programmatically generated (wavesurferjs)
Fine, however, the recent few updates (.11.x) have apparently deprecated that. I do understand where they are coming from, it fits the whole React logic.
However, how will we now ever programmatically modify state/props from outside? Even when I want to talk directly to the parent, which should be allowed.
The changelog tells me in this case the MUSIC variable would have become a descriptor, however, in consoles out the exact same object as far as I can tell. And the documentation says nothing about this descriptor and even less about alternative possibilities.
(http://facebook.github.io/react/blog/2014/07/17/react-v0.11.html#descriptors)
So, if I have two divs
<div id="main-div"> <button></button></div>
<div id="music-div"> </div>
And want to keep them separate, how would I go about giving two parallel parents each-other props?
I don't want to put both in one react div, which would not even solve my problem, because, how would the button in main-div give the props to music-div?
Or would their conceived alternative be to just create a new instance on that id and hope it diffs to 0?
The update states:
"You could store that reference and then call functions on it (eg
component.setProps(...)). This no longer works."
However, that still does work, with (0.11.1) so I don't understand what they are talking about?
You've got several options.
Wrap both main-div and music-div with an "Application" component. Pass a handler down that changes its state so the div's are re-rendered.
Use an event bus to dispatch and listen to events. Basically a component exposes its private setState/setProps() methods to the event bus in a listener. The other component dispatches an event that triggers that listener.

Categories