Is there any approach to use Fabric.js and Redux together? Fabric.js state should be used as part of store, but it isn\'t immutable and can mutate itself by user canvas inte
I found some solution. I try to describe, but sorry for english. Because there is no immutabity in Fabric.js it's hard to implement state management with redux. As far I understand default solution is to use fabric.loadFromJson function for push new state and serialization for pull and store for next manipulations such as actions history. But in this case JSON parsing will be bottleneck if you want to work with images, because they will be stored in Base64 data-uri.
The way is a bit hacky, but it works for me. I was replacing inner array of objects of fabric.js (fabric._objects) and invoking render everytime when something happens on canvas, e.g. moving objects by mouse.
First of all, my state is immutable now via Immutable.js, i.e. i have to return immutable List in my reducers. But elements of these lists are not immutable, it is just fabric.js objects stored in order that they should render. My state consist of objects list, selection list and several helpers objects that represent e.g viewport state (zoom, panning). Object state list keys used as ID of objects in actions. There is structure of my root scene reducer.
const sceneReducer = composeReducers(
whetherRecordCurrentState,
combineReducers({
project: undoable(
composeReducers(
projectActions,
combineReducers({
objects,
params,
counters
}),
),
{
limit: historyLimit,
filter: combineFilters(
recordFilter,
excludeAction([
'CREATE_SELECTION',
'CLEAR_SELECTION',
'SET_WORKSPACE_NAME',
'SET_WORKSPACE_ID',
'SET_WORKSPACE_TYPE',
'SET_TAGS',
]),
)
}
),
selection,
meta,
viewport,
recording
}),
selectJustCreatedObject
);
It implements any fabric.js possibilities including async functions such as applying filters. Also I use redux-undoable package and it allows implement unlimitted undo/redo history. It also allows implement not stored actions, such as opacity changing by slider (all intermediate states will be not stored). Since I use immutability I can push new history state with only one changed object to save memory. There is my state
https://i.gyazo.com/fcef421e9ccfa965946a6e5930e42edf.png
See how it works: in fabric.js I handle event with new object state. Then I dispatch action with that state as payload. In my actions I can create new fabric objects or pass updated objects. All async operations (filtering, changing image source) performing in actions and pass to reducer ready new object. In reducers there is access to my fabric.js objects factory that creates deep copy of object with one distinction. I patched fabric.js (monkey patching, but you can use prototype extending) and it does not serialize images to base64 anymore. I implement it by overriding method Object.toDatalessObject(), that returns same json without images data. Instead source data-uri image data it storing link to HTMLElement object by manually setting Image._element. I.e. after changing images coordinates new image object will have same _element. It allows to save memory and accelerate application.
After all, my container for fabric.js is React component. It connects with redux and after commiting change invokes componentWillRecievProps method. In method I catch new state, create copy with my factory (yes, there is double copying, it should be optimized, but it works fine for me) and pass it to fabric._objects and then I invoke render. I hope it helps.