I have an application using heavily HTML5 canvas via Fabric.js. The app is written on top of Angular 1.x, and I am planning to migrate it to React. My app allows writing text and drawing lines, rectangles, and ellipses. It is also possible to move, enlarge, shrink, select, cut, copy, and paste one or more of such objects. It is also possible to zoom and pan the canvas using various shortcuts. In short, my app utilizes Fabric.js to its full extent.
I couldn't find much information on how to use Fabric.js together with React, so my concern is that 1. is it possible without major modifications, and 2. does it make sense or should I instead use some other extensive canvas library that has better support for React?
The only example of React+Fabric.js I could find, was react-komik, which however is much more simpler than my app. My main concerns are the event processing and DOM manipulation of Fabric.js, and their effect on React.
There seems to be also a canvas library for React, called react-canvas, but it seems lacking a lot of features compared to Fabric.js.
What do I have to take into account (regarding DOM manipulation, event processing, etc.) when using Fabric.js in a React app?
There is also react-fabricjs package which allows you to use fabric objects as react components. Actually, Rishat's answer includes this package, but I don't understand how it is supposed to work as there is no 'fabric' object in the react-fabricjs (he probably meant 'fabric-webpack' package). An example for simple 'Hello world' component:
Even if you don't want to use this package, exploring it's code might help you to understand how to implement Fabric.js in React by yourself.
I've used Fabric for a proof-of-concept project and the general idea is the same as for, say, D3. Keep in mind that Fabric operates over DOM elements, while React renders data into DOM, and usually the latter is deferred. There are two things that will help you make sure your code works:
Wait until component is mounted
To do that, place your Fabric instantiation into
componentDidMount
:Placing Fabric constructor into
componentDidMount
ensures it won't fail because by the moment this method is executed, the DOM is ready. (but the props sometimes aren't, just in case if you use Redux)Use refs to calculate actual width and height
Refs are references to actual DOM elements. You can do with refs what you can do with DOM elements using DOM API: select children, find parent, assign style properties, calculate
innerHeight
andinnerWidth
. The latter is precisely what you need:Don't forget to define
refs
property ofthis
. To do that, you'll need a constructor. The whole thing would look likeMix Fabric with component state or props
You can make your Fabric instance react to any component props or state updates. To make it work, simply update your Fabric instance (which, as you could see, you can store as part of component's own properties) on
componentDidUpdate
. Simply relying onrender
function calls won't be really helpful because none of the elements that are rendered would ever change on new props or new state. Something like this:Simply replace image rendering with the code you need and that depends on new component state or props. Don't forget to clean up the canvas before rendering new objects on it, too!
We had this same issue in our app of how to use Fabric.js inside of react. My recommendation is to treat fabric as an uncontrolled component. Have fabric instance that your whole app can talk to and make fabric calls and then when anything changes use
.toObject()
call to put the whole fabric state into your Redux store. Then your React app can read fabric state from your global Redux state as you would do in any normal React app.I can't get an example working in the StackOverflow code editor but here is a JSFiddle example that implements the pattern I am recommending.
Whoops!
In case you were coming here expecting answers about Microsoft Fabric, you're in the wrong place. You should be looking for office-ui-fabric.
(Irrelevant and slightly embarrassing original answer removed.)