CLJS/Reagent: React does not recognize the prop on a DOM element - clojurescript

I see there are lots of similar question in Javascript/React but I'm having this issue with CLojureScript/Reagent and I'm at a loss about how to solve it without messing up all my app-state names.
So I'm getting these warnings in the browser console:
react_devtools_backend.js:2430 Warning: React does not recognize the `showWarning` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `showwarning` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
These appear to be coming from the name of an item in the app-state:
(defonce app-state (reagent/atom {:show-warning "none"}))
The only solution I've found is to rename the item from :show-warning to :showwarning but this leads to a bunch of hard to read items.
I don't have any real control about how Reagent renames keys like :show-warning to "showWarning" but it seems this, being basic to how React works, would have a cleaner way to handle this.
Thanks!

React really shouldn't be able to see your state or care what's in it. My guess would be that you are rendering your state somewhere in the props positions. Most likely somewhere in your code you have [:div #app-state]. Possibly you were trying to render the state and should've done [:div {} #app-state].
This is perhaps a bit of a gotcha with Reagent, since [:p #im-a-string] would render the string but [:p #im-a-map] will treat the map as props.

Related

Autodesk Viewer: Show a list of elements with specified Database IDs

The functionality I seek is very similar to the default ModelStructurePanel model browser, except that I need to list only a subset of elements, by passing a list of dbIds of the elements I want listed. By clicking on an element on that list, have the view focus on that element.
I figure there might be two ways of achieving this by using the ModelStructurePanel (although I'm open to using something else):
Creating a new instanceTree with only the specified elements, then doing something like viewer.modelstructure.setModel(newInstanceTree)
Overwriting the ModelStructurePanel.shouldInclude method to hide all elements but the specified ones.
I have googled for Viewer code boilerplate that would provide this functionality, but have not found it. Any help is very much appreciated.
There is a basic sample here very close to what you described, and I would go with customizing just one action instead create a new one, seems easier.

Using Draft.js with Reagent

Does anyone have any luck adapting Draft.js for Reagent? There's pretty heavy editing issues if Draft.js is imported right away via reagent/adapt-react-class. Cursor jumps, disappearing symbols when you're typing, onChange calls with incorrect EditorState, you name it.
People are reporting problems like this in clojurians/reagent Slack channel, but it seems there's no solution so far.
Any help would be greatly appreciated.
Okay, thanks to tonsky, I got the answer. Reagent/Rum are using deferred rendering with requestAnimationFrame, but Draft.Editor should be re-rendered immediately when editorState is set.
All we need is to call forceUpdate for editor parent component whenever editor onChange is invoked:
:editorState #editor-state-atom
:onChange (fn [new-state]
(reset! editor-state-atom new-state)
(.forceUpdate #wrapper-state))
Code example is for Reagent, solution for Rum is identical
Just an idea (more of a comment, but I can't provide comments yet), because -- if I remember correctly -- the :content-editable attribute is treated in a special way within Reagent:
Since the problems seem to occur when the Draftjs editor is called from Reagent, would it not help to convert the Reagent-component to a React-component (using reagent/reactify-component), and then use this "reactified" component as a react-component within Reagent, using reagent/create-element? I would assume that Reagent then refrains from meddling with the Draftjs editor.

reagent render multiple components

I've got two reagent components that will display on different pages of my web application. Tested individually, they both work exactly as they ought. When I try to render both of them, only one is displayed.
Here is the clojurescript:
(defn mount-components []
(r/render [#'password-component] (.getElementById js/document "password"))
(r/render [#'test-component] (.getElementById js/document "test")))
(defn init! []
(mount-components))
On the back end I have this:
(defn password-page []
(layout/base
[:h1 "Change your password"]
[:div#password]))
(defn home-page []
(layout/base
[:h1 "Hello!"]
[:div#test]))
(defroutes app-routes
(GET "/" [] home-page)
(get "/password" [] password-page))
After I compile the clojurescript and look at the pages, only the password component displays. If I swap the order in the mount-components function, so that test-component is first, only test-component displays and not password-component.
How do I display both components at the same time?
I think the problem is possibly a type of design flaw in your implementation. In react programs, it is changes in your state atom which will trigger a re-rendering. From the provided code, it isn't clear what the 'trigger' is which will cause a re-rendering to occur. It might hlep to actually see the full page code to see how you are calling the javascript - it could simply be that init is only running with the first page load and not for every page. What could be happening is
You visit the first page. The mount-components function is called and finds only one of the div elements targets for your component, so you only see one component rendered.
You visit the second page. Now you have a page with the 2nd div element, but the problem is nothing in your local state has changed to trigger the re-rendering of components, so you don't see the second component. Basically, something needs to tell reagent/react that you need to re-render the component.
With reagent, I think it works much better if you implement the client side as a single page app (SPA). You use a reagent atom to store your app state and use the values in that atom to determine/control what/when components are rendered on the page. Calls back to the server are predominately used to just pass data back and forth rather than page html (gross simplification and overlooks things like client side rendering).
If you really want to do it the way you are, you probably need to put your render commands in different functions and then have different javascript calls in each page which will call the corresponding 'run' function which will render the component. Alternatively, you could link your components to some sort of state flag which is updated when a new page is loaded so that the mount-components function is re-run when a new page arrives.

Dom-repeat and neon-animatable in Polymer

I have a same problem with use dom-repeat for neon-animatable in Polymer.
So, about my problem. When I dynamic building page, I use dom-repeat for building a certain number of pages. In code that look like
<neon-animated-pages id="views"
class="flex"
selected="[[selected]]"
entry-animation="[[entryAnimation]]"
exit-animation="[[exitAnimation]]">
<template is="dom-repeat" items="{{dataView}}">
<neon-animatable id="{{item.id}}">
<inner-content data="{{item.content}}"></inner-content>
</neon-animatable>
</template>
</neon-animated-pages>
After builded this page, I have a page which contains a certain numbe neon-animatable pages, but after first click on button for view next page , animation don't work, but if click on button more, animation work fine.
So, I can't understand why first animate don't work
If who have some mind about how resolve this problem, I will be grateful
P.S About my observations
When use in static code like this
<neon-animated-pages>
<neon-animatable>page 1</neon-animatable>
<neon-animatable>page 2</neon-animatable>
<neon-animatable>page 3</neon-animatable>
</neon-animated-pages>
Animation it is running the first time
This appears fixed for a future release: https://github.com/PolymerElements/neon-animation/issues/55
Details:
I ran into the same problem and it appears related to timing on when the light DOM children are created.
Short answer: on your custom element add an attached function that sets the selected page.
attached: function () {
this.async(function () {
this.$.pages.select(0);
});
}
Long Answer
When a template dom-repeat is used inside the neon-animated-pages, at the point the web component is initialized it is not able see the details of the light DOM children.
Using static code the tree is seen as:
neon-animated-pages
- neon-animatable
- neon-animatable
- neon-animatable
Using dom-repeat at initialization it looks like:
neon-animated-pages
- template
Because of this when the neon-animated-pages tries to set the selected page it get undefined because it is not able to see the neon-animatable components. These get created later. This behavior is described in the documentation https://www.polymer-project.org/1.0/docs/devguide/registering-elements.html which says
Note that initialization order of may vary depending on whether or not the browser includes native support for web components. In particular, there are no guarantees with regard to initialization timing between sibling elements or between parents and light DOM children. You should not rely on observed timing to be identical across browsers, except as noted below ... This means that an element’s light DOM children may be initialized before or after the parent element, and an element’s siblings may become ready in any order.
As the light DOM children starts getting filled in a call is made to the observer function "_updateSelected" on IronSelectableBehavior, which is the parent of NeonAnimatedPages, to update the selections based on the content updates.
However, NeonAnimatedPages also maintain internal references to identify selected items and uses the observer function "_selectedChanged" to setup references and control animations. Essentially it decides to display an animation based on whether a previous element is selected. Based on the tree above, when this method is called at initialization it cannot see the full tree and it sets previously selected to undefined. As the light DOM is getting populated, this handler is not getting called because the selected value is not changing only the content is changing. The consequence is that while IronSelectableBehavior correctly knows what is selected, NeonAnimatedPages still thinks nothing is selected, and as you make your initial screen change it treats it as an initial load because it thinks nothing is selected and suppresses the animation.
The solution essentially waits for the entire tree to be built and then sets the selected value so that all the event handlers are able to correctly see the light DOM children and set up internal references.
As Ade mentioned, this was a logged issue in neon-animation, but it has since been fixed. Update the component (bower update, if you're using bower) to resolve the issue. I would have added this as a comment to Ade's answer, but I don't have enough reputation yet

Properly instantiating a template instance so that everything is present (model data, filters, event handlers)

I'm a bit of trouble instantiating a custom template, and making all the bindings work. My custom element which has to do this is quite similar to polymer/core-list, with a few differences. Like core-list, the parent adds the element invokes my custom element, and adds a template as its content, as seen here.
Unlike core-list, the element adds an id to this template, and creates a few templates which refer to that one, as seen here. Finally, when the time comes, these new templates are used to create a few elements and add them in the dom.
That's all fine and good, and mostly, it works correctly. The model data is used to fill the resulting element correctly, and the default filters work, thanks to the PolymerExpressions used as a bindingDelegate. However, event handlers do not seem to work.
I don't know whether the handler function can't be found in whatever scope is used, or something else is at play here. The only thing I currently know is that the on-tap attribute value is empty when I look at the polymer-icon-button through the web inspector. With a very similar usage using the core-list, the event handler works. The web inspector there shows the polymer expression as the value of the on-tap attribute. And both handlers are defined in the parent element which contains the invokations of core-list and my element, and the templates which are passed to the corresponding contents.