Let's say I have two vue modules:
parentModule.vue
<template>
<svg>
<child-module v-for="i in ['a', 'b']" :key="i"/>
</svg>
</template>
...
childModule.vue
<template>
<g>
//...some valid svg content
</g>
</template>
...
Now, it works decently. The issue is, unfortunately, that in development mode I get a warning about mismatched nodes - apparently Vue doesn't like using svg elements. I have a feeling it messes a bit with some things in development during re-rendering. Is there any way around this?
Edit
After dabbling for a bit it seems the issue might be with something different - on a line of how canvas property is being updated. As I couldn't replicate it in CodePen (I guess version of vue might have a different logging level?), here is a working example: https://github.com/msdsk/so_svg_example
In your example the problem is due to the usage of Math.random() in pages/index.vue (and has nothing to do with SVG). As a result the server-side rendered page doesn't match the virtual DOM produced on the client-side, leading to the observed warning during hydration.
From the Vue SSR documentation:
Hydration refers to the client-side process during which Vue takes over the static HTML sent by the server and turns it into dynamic DOM that can react to client-side data changes.
...
In development mode, Vue will assert the client-side generated virtual DOM tree matches the DOM structure rendered from the server. If there is a mismatch, it will bail hydration, discard existing DOM and render from scratch.
Related
I wonder, is it possible to create a visual no-code builder to work with JS components (e.g. React JSX) if them are manually hard-coded before?
Let me explain what I mean.
Suppose a simple React component pages/index.js previously written by a developer manually:
function HomePage() {
return <div>Welcome to Next.js!</div>
}
export default HomePage
How can we change such a component in the front-end using a visual builder?
For example, we want to add a new HTML element (e.g. H1) inside the existing div.
As I can understand, the builder first needs to know in which JS file the HTML markup is stored to update it. So we can add id="homepage" to the div first and then store a relation as a config like
{"homepage": "pages/index.js"}
And now if we add a new element inside <div id="homepage">, the builder adds the element to the DOM of the div at the client, then takes the whole updated DOM of the div and writes back to the file index.js according to the config
Ok, but the file contains not only HTML markup - it's JS (React) code.
How to keep all the JS code e.g. function HomePage(), return, export default and so on ?
As an option, we can separately load all the JS code as HTML including non-HTML code as #text nodes. Then update the DOM and re-write everything back to the file.
But it sounds complicated and may cause unexpected issues.
So what solution would be the best?
Or maybe there is a ready React-specific solution?
Or maybe it's a bad idea at all to parse and re-write manually hard-coded components by visual builder and the only solution is to store everything as JSON like "homepage":{"div", {"class":""}, "Welcome..."} which is more easy for re-writing ? (but requires a new render)
It depends
I believe the answer that you are looking for very much depends on the level of flexibility that you want your no-code builder to have.
Depending on that, your project could benefit of the trade-offs and advantages of different solutions.
Let's briefly remember that basically a React component will need some props that then will be taken through a render template and output a working HTML. This is assuming a basic case where you don't need your react components to be smarter. Additionally, JSX is just sugar coating over function calls, so you could basically just compose functions to output a React component independently of using the JSX syntax. Hence no need to declare HTML, just changing the output of your no-code tool to JS instead of HTML.
For example, if you can modify how the no-code tool render, you can specify that when moving an element inside another you basically:
Highly Flexible & Customisable
In a highly flexible setup, I will recommend going through the last option you numbered, having a Data-Driven UI is the most common of the cases for complex systems. For example, Figma has an option to export the designs as react components, you can read how they do it here. Basically they take the tag output from figma which is a JSON of tags and create some React componets using templates. If you define "you own UI language" you could have quite a good control over what blocks you can build and define the way of interacting with them (e.g. adding a img component could be quite easy if you know the props and what it needs to render, then creating a React template for it is easy).
Limitations: you require to plan quite well the API of the parser and interaction between different sets of items.
Simple no-code builder
For simpler scenarios you could go with the first approach that you mention, you won't even need to add ids, some tools like React Developer Tools can already inspect the VirtualDOM to understand which part of the render belongs to which React Element (using react internals, which could take some time to understand, but for example you can inspect in the rendered how they use the data-reactid for identification). Knowing this, you can already define the template functions for the render() method (so the JSX output), and separate it code wise, so that when you generate the code, the HTML template is split from the React code as much as possible.
Silly example of how it could look:
// htmlBlockTemplate.js
export const helloPageTemplate = (props) =>
`<div> <h1>${props.title}</h1> </div>` // This can be generated from the `no-code`
// page.jsx
export const Page = (props) => {
return helloPageTemplate(props)
}
Using functions it could look like:
const Page = (props) =>
return React.createElement('div', null,
React.createElement('h1', title: prop.title, `The title ${title}`)
);
}
Limitations: eventho you could, adding custom components (like another React Component or a web component), it becomes more difficult since you will also have to deal with the import graph and probably.
Inevitably you will need to tweak how the render of the component works (rather by creating a parser from your UI language, or changing how the react component is written. If you have a good control of AST, then writing a parser for either of the cases should not be a problem.
How can I access a rendered React component's displayName property in HTML? There seems to be some way to do it given React DevTools, but I've been reading through the source code for that package and can't figure out how they're doing it. I recognize that this might be cross-referenced from the associated JS file, but I'm hopeful that's not the case.
Context: I have a script that identifies certain components in the prod version of our app and adds a red border around them, which works fine. In React DevTools I can see the displayNames of those components even on prod (they're not minified for whatever reason, which is beyond my scope of curiosity right now). I'd love to be able to surface those displayNames in addition to the added border.
I just started learning about the HTML custom elements, and through reading a series of intros, tutorials, and documentation, I think I have a good handle on how it works, but I have a philosophical question on the proper way to use or not use the <template> tag.
Custom elements give you the ability to encapsulate new functionality, simplifying the structure of your HTML document, and allowing you to simply insert a <my-custom-element>...</my-custom-element> tag instead of <div class="my-custom-element"><span class="part1">...</span><span class="part2">...</span></div>.
The class definition for the element then sets up the structure and functionality of that element. A bunch of the tutorials then describe how to use <template>...</template> and <slot>...</slot> to set up the contents of the custom element. You would then have to then include the template code in every HTML document in which you want to use the element rather than setting it up in the custom element class's constructor. Doesn't this run counter to the fact that custom elements help simplify and encapsulate functionality in a way that makes them more portable? Or am I misunderstanding the proper usage and/or placement of the template within the document?
Looking through SO, the closest I can find to addressing this is this question:
How to stamp out template in self contained custom elements with vanilla js?
But the answer essentially sidesteps this all together and says "Don't use <template>," and so doesn't really clear up my confusion.
Actually <template> elements can be imported from another document via HTML Imports, along with the Javascript code that will define the custom element:
<link rel="import" src="my-custom-element.html">
...
<custom-element></custom-element>
So it doesn't need to be included in a every HTML document. This post shows a minimal example.
HTML Imports are implemented only in Chrome and Opera. If you want to use them in the with Firefox and Safari you'll need to use the HTML Imports polyfill.
On the other hand and for the moment, Mozilla and Apple don't intend to implement HTML Imports natively in their respective browsers. Therefore they recommend to define custom elements with pure Javascript modules (with import or <script src="...">), and promote template literals strings instead, which offer some advantages (variables, functions), but are sometimes more complicated to code in an IDE (because of their string representation).
Maybe in the future standard HTML modules will be adopted by all browsers, and <template> will come back in the spotlight...
Note that without HTML Imports you can still import yourself some HTML documents with fetch():
fetch( "template.html" )
.then( stream => stream.text() )
.then( text =>
customElements.define( "c-e", class extends HTMLElement {
constructor() {
super()
this.attachShadow( { mode: 'open'} )
.innerHTML = text
}
} )
)
Update 2019
HTML Imports won't be supported natively after Chrome 73. You should then use the other solutions listed above (the polyfill, an alternate module loader, JS import, or a direct download with fetch).
Disclaimer: I'm an author of the rich-component library mentioned below.
After some time of experimenting with custom elements and recently raising a full blown project based solely upon them I'd like to share my insights on this:
any component tiny as it is, is a candidate to grow to some beast
HTML part of it may grow to a point where it is very non-convenient to keep it within JS
do use template, built and parsed once and from that point cloned and injected into the shadow root - this is the same best practice as to use document fragment instead of mutating a living DOM
if the template contents should be changed from component's instance to instance - some kind of data binding framework may be used, and if minimalist approach on those is taken - it might still be easier and more performant to deal with a cloned-from-template document fragment than operate on string or template literals
In order to not write the same dozens of lines over and over again I've prepared rich-component library, which:
normalizes some API for template provisioning and all those 'clone template, create shadow, inject template's content into it' lines of repeating code
known to fetch html contents when html URL is provided
caches the templates so the fetch is done only once
I'm working on an app for a client who wants to display Zendesk Support article data with richer content than the API/tool supports. The API, powered by a basic WYSIWYG editor on Zendesk, is able to serve up an HTML blob with <p> and <img> and <a> tags. But it's unable to, say, easily embed a video, show an image gallery, or link to another location in the app without necessitating a hard refresh.
So we decided to build out a small amount of DSL syntax whereby the client could type something that would be parsed on our app and turned into the richer content. For example:
[[ video src="youtube.com/whatever" ]]
might, on our app, be parsed out, and replaced with an iframe embedding that video directly in the article.
I've got the above example working just fine, using all sorts of fun/problematic RegExp magic.
I'm aware that parsing HTML with RegExp is a major anti-pattern, and I'm getting around it by not really parsing HTML per se -- I'm just looking for [[...]]).
Basically, I parse each code out, determine what to replace it with, pass back that component, and then call ReactDOMServer.renderToString(replacementEl) to insert the resulting HTML into the article HTML string, which is then rendered to the page using dangerouslySetInnerHtml.
This is working great, despite all the obvious messiness issues. (I know, but the client wants a very feature-rich solution, and wants to use the Zendesk API).
Here's the problem: This solution only works when rendering HTML. It doesn't work when I try to render, for example, a <Link> (from React Router) or a home-made <Gallery /> component.
So I'm trying to figure out if there's a good way to do this. One that ideally doesn't involve going full RegExp on the HTML blob (which is obviously a mess).
I'm guessing that the solution will involve removing the codes, converting the code-free HTML string into actual React components (as opposed to just spitting the string into another element), and then, based on some sort of ledger of where the short codes go in this new React component, inserting the contents into the page.
But I'd appreciate some suggestions if anyone has encountered a similar problem. I want to do this in a way that isn't totally crazy and brittle, though it's obviously an inherently messy problem.
EDIT: To clarify, when I say it doesn't work, what I mean is that the JavaScript associated with the React components doesn't run. If I replace a code with a <Link to="whatever" />, for example, ReactDOMServer.renderToString successfully turns that component into its final HTML (an <a> tag), but nothing happens on click of that tag, because the JavaScript that's actually changing browserHistory on a regular click just isn't run.
So maybe the more pointed question is:
Is there a way to insert React Components into an HTML string in such a way that they retain their JavaScript functionality and don't just become HTML?
I'm trying to optimize my big ReactJS project and I've stumbled upon a conclusion, that React code in the browser is not taking any advantage of server rendering. I'm sure that server-side rendering is working properly, I can see data in the page source returned from server, with all the data-reactids.
Setup: server-side code is rendered in Node. React version 15.0.1. I'm using ClojureScript and rum, but it shouldn't matter in the end
Test environment: Using React.addons.Perf, wrapping it around initial mount on the client side.
Test 1: proper server-side rendering
Total time around 1.8s, Inclusive Time of root component about 1.5s. React.addons.Perf.printWasted() shows nothing.
But what is the most puzzling part for me is that React.addons.Perf.printOperations() shows one operation: "set innerHTML" with the whole app's html (e.g. <div data-reactroot=\"\" data-reactid=\"1\"><div id=\"wrapper\"...). Like the whole server-side rendering result was discarded and this client-rendered HTML is inserted in the DOM, probably causing re-rendeding.
Test 2: invalid checksum; by purpose I've added some extra attribute on the server-side only.
Of course the "React attempted to reuse markup in a container but the checksum was invalid." warning appears.
However, everything else is similar to the Test1 above! Similar times, and also the single "set innerHTML" operation. This confirms for me that even if checksum is correct, React ignores pre-rendered markup (because it seems to do the same operations in the similar times whether checksum is correct or not)
Test 3: turned off server-side rendering; returning empty mount-point container.
Times about 0.2s longer. The only real difference now is that there are many operations, most of them update attribute or update styles and one set innerHTML with {"node":"<not serializable>","children":[],"html":null,"text":null}.
Question/Problem
I don't know what to think about it. React advertises SSR as a way to speed up application bootstrap, as it does not need to generate DOM tree again (or rather: insert it in the document; it still need to be rendered in order to compare checksums, right?). But for me it seems like nothing is gained.
Can someone who is successfully utilizing SSR tell me what are his/her reading in printWasted() and printOperations()? The only thing I managed to find was this audit: https://github.com/reddit/reddit-mobile/issues/247#issuecomment-118398416 (only printWasted, though) - none of my tests shows any entires in printWasted.
Well, it all was simple: after upgrading to React 15.4.2 Test 1 shows no operations, as I expected it to in the first place. So in the end it all was issue of (not so) old library version. Yet the times of mount are still very similar.