What we do
Outline ● Focal: good and bad points ● Reusability problems ● Experimental challenges ● Embrace: the solution to the above ● The current state of things
Let’s start with https://github.com/grammarly/focal
Focal enables “two-way binding”
But two-way binding has some issues ● Hard to reuse components: coupling of View and Model logic ● Hard to do action—generic logic: logging, filtering, throttling, etc.
Action messages are better
Action messages are better
Action messages are better
Action messages are better
The way we do state management ● Component state inside an Atom ● Component publishes actions ● State manager/VM listens for actions and updates state ● Rx.js: complex side effects management and synchronization of states ● Fp-ts: complex state updates
Problem: nested state
Problem: nested actions
Problem: uncontrolled side effects
Solution hints: ● First-class support for action messages ● Better state composition ● Better action composition
Maintainability
Props interface on component?
Or state interface on manager?
Solution hints: ● One place to define state and action interfaces ● VM <- UI is bad ● VM -> UI is good
Experiments A/B
Experiments with if-else
If-elsing problems ● It scales badly (think >100 experiments) ● Easy to add, hard to remove ● Easy to introduce experiment conflicts ● Need to involve multiple teams to do each experiment
Solution hints: ● UI as a traversable data structure ● Experiment as a function from one UI to another ● Type safe UI changes to avoid breaking changes
Introducing Embrace ● “Embrace” as in “hug the UI and squeeze any last bit of side effects from it”
Introducing Embrace ● “Embrace” as in “hug the UI and squeeze any last bit of side effects from it” ● A library for UI composition with a focus on type safety and immutability ● Makes UI representation truly declarative ● Uses React but triggers re-renders via Rx.js observables and Focal internally instead of relying on React’s VDOM
UI Part UI Part S S S Obs<State> A A A Obs<Actions>
UIPart
Flow Flow S S S Obs<State> S S S Obs<Actions>
Flow
Mounting ● Given a UIPart ● And a Flow that matches in action/state types ● Embrace can “mount” it ● Producing the ReactElement ● mount(UIPart, Flow) = ReactElement
Embrace app model
Counter Example
Counter Example
Unlike: ● Pure react Embrace is declarative; no side effects allowed in components (No hooks! Well, maybe a few) ● Redux: no single state; everything is observable by default; no need to manually pass props ● Cycle.js Embrace is type safe, does not rely on selectors, is declarative
Composing UI parts UI Part UI Part UI Part
Composing UI parts ● A programmer who uses Embrace composes an App’s UI from smaller UI Parts ● Node: a UI Part that does not have slots ● Grid: the same as a Node but has named slots ● Knot: fills in a Grid’s slots with other UI Parts ● List: renders the collection of parts ● Union: renders one part at a time from a union of parts ● etc.
Composing UI parts
Composing UI parts
Patching UI with Embrace App New App Patch
Patching UI with Embrace
Experiments through Patching
Tales from production
Tales from production
Tales from production
Tales from production
Tales from production
Tales from production
Tales from production
Tales from production
Tales from production
Embrace today ● Spreads through Grammarly UI platform ● Used by Grammarly Extension and Web Editor ● Continues to evolve ● Not ready for public release yet ● Challenges: animations, lists, animations in lists
● Gives us truly declarative UI logic that composes ● Provides our and other Grammarly teams full control of the UI after we reuse Embrace components ● Allows us to manage breaking changes Embrace today
Join us! Help make Embrace and other cool stuff!
Thank you!

"The Story of Declarative React at Grammarly: From two-way data binding with Focal to a custom implementation of UI as a tree data structure", Oleksandr Suhak