Using React and Canvas to create a dynamic editor - reactjs

I'm working on an editor where a user adds, updates, and removes elements from a Canvas. I'm using FabricJS to actually do the drawing, but I don't think this matters.
Configuration is stored in POJOs in a redux store, and this is loaded into the canvas. As the user updates the canvas, either through the Fabric controls or through custom controls in React, these updates are emitted as events from Fabric and are propagated into my redux store.
The issue I'm having is finding a good way to handle state. The single biggest issue I'm dealing with is that each state update triggers a re-render which causes my entire editor canvas to flash as the canvas is torn down and re-rendered.
I have already implemented Adam's fantastic post on "persistent layouts" in NextJS. This actually solved another issue I had with flashes caused by page routing. However, I don't think I'm able to apply the same pattern to this.
I have tried the following:
memo - This doesn't work well for canvas as it still shows the flash, even if props don't change
useRef - Remove any reactive state that could cause a re-render from my component (and parents). However, I'm not sure how to then propagate state changes to and from the canvas.
In addition to the above, try using a global event bus for communication. This seems very annoying to work with and debug, so I want to ask before I implement this.
Has anybody solved a problem like this before? Specifically, how to get around re-rendering a Canvas while still enabling it to have some access to Redux state.
I'd be happy to provide a sample repo of the issue if this is unclear.

Related

React asynchronous state update causing unresponsive User Experience

My top-level React component provides a context to all its children which is used to render UI. The context is an array, and a UI element is generated for each element in the array. Users can delete items from this array using buttons placed around the app and throughout the component tree.
The problem is when an element is deleted, the context takes a really long time to update and the app feels unresponsive. I know it's because it's async, but it's causing UX issues.
How can I make my app more responsive? Is there a way to force a state update?
I'm not sure how to approach fixing this. Would a state management lib work?
Turns out this is an issue with Chrome being a memory hog. I cleared caches and cookies, restarted chrome + server, and everything started working. What a silly thing to spend hours profiling your react app for! Hopefully this can help someone else.

How to check when the react components get rendered or rerendered

Is there any way to get logs of which all React components get rendered or rerendered when any event/action is performed. I just want to check whether any unwanted React component is getting rerendered. I know we could achieve this by adding consoles in render function, componentWillReceiveProps or componentWillUpdate. But I have number of components in my webapp. Adding console statements in each component would be a mess. Is there a better way?
A nice option is to visualise React components in the chrome timeline. This lets you see which components exactly get mounted, updated, and unmounted and how much time they take relative to each other. That feature was added as part of the release of React 15.4.0. You can take a look at the official blogpost to get a better insight, but summing up, those are the steps to get it up and running:
Load your app with ?react_perf in the query string (for example, http://localhost:3000/?react_perf).
Open the Chrome DevTools Timeline tab and press Record.
Perform the actions you want to profile. Don't record more than 20 seconds or Chrome might hang.
Stop recording.
React events will be grouped under the User Timing label.
After that, you will get a cool visual representation of mounting, updating and unmounting of your different components over time
In addition to rauliyohmc answer. Profiling tool he described is really helpful and powerful, but if your task is just checking if there are unnecessary re-renders (i.e. ones which did not lead to DOM changes) you can use nice and simple react-addons-perf tool. It has printWasted method which will provide you info about wasted re-renders.

React internals: How does React know how to rerender an iframe correctly?

I was experimenting with React and rerendering iframes and I was not sure how React correctly rerenders iframes, especially ones which point to a text editor. Here is a jsFiddle showing this:
https://jsfiddle.net/augburto/fkqnm329/2/
The text editor I point to is not important but what I am doing is when you click Trigger Update, it will call a setInterval which will constantly set a new state and thus trigger a rerender.
What I thought might happen is that when I'm typing in the iframe which has a textarea, it will inevitably rerender and thus make me lose my text editor position but somehow I am able to type seamlessly without any issues despite seeing the rerenders come through in the console.log. Note I'm not suggesting it should do this -- I'm just wondering why it doesn't do that. I know React internals do some smart things like transactions but I wouldn't expect it to maintain my cursor position or what I have selected.
So how does React handle rerendering iframes more specifically? I have read articles (i.e. this and this but they don't shed a lot of light on it I feel). Is it any different from regular DOM elements?
Okay so after some time, I think I believe I discovered the answer. In general, because React just creates a virtual DOM and does a diff behind the scenes, it really is only looking at the iframe element and not what is generated. That is a completely separate thing where the iframe creates its own browsing context. Reading the MDN documentation on iframe was really informative:
The HTML element represents a nested browsing context, effectively embedding another HTML page into the current page. In HTML 4.01, a document may contain a head and a body or a head and a frameset, but not both a body and a frameset. However, an can be used within a normal document body. Each browsing context has its own session history and active document. The browsing context that contains the embedded content is called the parent browsing context. The top-level browsing context (which has no parent) is typically the browser window.
I think React does do some special things when it comes to parsing an iframe but overall it doesn't deviate too much in general.
Also something that I think might be informative is how React handles input elements in general -- take a look at Controlled Components in general for Forms as React has a completely separate and special way of handling those cases.

Flux without React .. is reliable option?

What I understood from Flux documentation that it recommend re-rendering view for any minor change raised by store. But what if I am using Flux without React that compliments re-rendering complete view by virtual DOM and rendering just diff.
What if I wanted to implement only Flux without React , won’t re-rendering complete view for every dam change in store be performance bottleneck .. ?
Flux does not recommend re-rendering view for minor changed raised by Store, but instead, empowers you to control when your DOM re-renders.
From the Flux Documentation,
The store updates themselves in response to action, but this does not affect the View.
The View updates only when the store emits a change event, which means, Flux gives you total control as to when you wish to update your View.
Also, all the Views do not get updated, only those (controller-views) which listen to specific variable changes inside the Store get updated. This makes your application more performant.
If you wish to follow the same architecture outside React, you can always do that, but it does not mean you will have to update the complete View on all changes inside your store, which implies, it should not become a performance bottleneck.
Your concern is correct: re-rendering the whole view for any state change will destroy your app's performance.
Re-rendering the view works well with React and Flux together because of React's ability to selectively update the DOM. When you change a prop on a component, React rebuilds the entire DOM in memory as a "virtual DOM", without pushing the changes to the actual DOM. Then it applies only the differences between this virtual DOM and the real DOM, making it a very efficient process.
If you want to use Flux without React, you'll need to manage the update process yourself. So, for example, if your state has 50 different values and you change one, you will need to identify the elements associated with that one value and update it accordingly, leaving the others untouched.

Issue with UI event when rendering component inside a web component shadow DOM

I'm facing some issues when rendering a React component into the shadow DOM of a webcomponent.
I wrote a small piece of code to turn a React component into a webcomponent, but I want to render the
React component inside the shadow DOM of the webcomponent. But in that case, it seems that React is not able to catch UI events (click, keyPress, etc ...) anymore.
Let's take an example, let say that I have a first webcomponent <awesome-timer /> that render the React component inside the webcomponent node, and another webcomponent <less-awesome-timer /> that render the React component inside the shadow DOM of the webcomponent.
Both webcomponents use the same React component. However the one rendered inside the shadow DOM does not work, because click events on the button of the timer component does not trigger the bound function.
I guess React is not designed to handle such case, but I'd love to get more details about it.
The code of the example is available here : https://gist.github.com/mathieuancelin/cca14d31184bf4468bc1
Does anyone have an idea about it ?
I know this is kinda late but, I believe your issue when you pass any attributes to a web component they instantly become strings Because that's all you can pass to a web component. Now of course you can convert or cast them back to there original data type, except functions because once stringified they loose there scoping, lexical and all.
Now to your main question, you are were trying to pass you child element through the Main web components slot. Now you have to remember that once you pass anything to a web component you now have to use the webs components methods and return types to manage whatever you pass. So yes passing react into a web component will not work they you expect.
You will need to go back to whatever tool you use to build your web component and deal with the slot logic there. Since this is a very old post as are web components. You might not have had access to the modern web component build tool's we have today. I found Stenicl allows you to build and manage your web components in Typescript.
A good option is to change your pattern a little bit and just return web components from your react app.
Or you can use another really cool to call Lit-HTML or Lit-element. I believe they may have combined there core libraries. Anyway these tool will allow you to combine Reactjs and web components where lit-html gives you access to methods simial to Reactjs's life cycle methods. Anyway some good stuff to check out if your stuck at this point.

Resources