Slide 1

Slide 1 text

Make it Declarative with React @koba04 / JSConf JP 2019

Slide 2

Slide 2 text

Toru Kobayashi @koba04 Twitter / GitHub Web Developer 2007 ~ Cybozu →Frontend Expert Team SmartHR →Frontend Advisor

Slide 3

Slide 3 text

ReactVoice ReactVoice. .render render( ( < <> > < > こんにちは こんにちは < /kyoko kyoko> > < > I I work work as as a frontend developer a frontend developer for for Cybozu and Cybozu and I I work work as as a frontend advisor a frontend advisor for for SmartHR SmartHR. . < /alex alex> > < >My Twitter and GitHub accounts are @koba04 My Twitter and GitHub accounts are @koba04< /alex alex> > < > I I'm also one 'm also one of of the organizers the organizers of of React React. .js meetup js meetup in in Tokyo and Tokyo and a contributor a contributor of of React React. . < /victoria victoria> > < >I I've been working 've been working with with React React for for 5 5years years. .< /victoria victoria> > < /> >, , { {} } ) ); ;

Slide 4

Slide 4 text

Agenda Benefits of Declarative Programming for UI Custom renderer of React Live Demo!

Slide 5

Slide 5 text

Declarative Programming for UI

Slide 6

Slide 6 text

Declarative Programming https://en.wikipedia.org/wiki/Declarative_programming In computer science, declarative programming is a programming paradigm—a style of building the structure and elements of computer programs—that expresses the logic of a computation without describing its control flow. Many languages that apply this style attempt to minimize or eliminate side effects by describing what the program must accomplish in terms of the problem domain, rather than describe how to accomplish it as a sequence of the programming language primitives

Slide 7

Slide 7 text

Why Declarative? What Not How How -> Compiler Abstraction layer Optimization in the underlying layer Primitive as domain

Slide 8

Slide 8 text

The logic of a computation without describing its control flow

Slide 9

Slide 9 text

DOM manipulation is based on imperative operations

Slide 10

Slide 10 text

Imperative const const view view = = document document. .querySelector querySelector( ('.view' '.view') ); ; const const addButton addButton = = document document. .querySelector querySelector( ('.add-button' '.add-button') ); ; // You have to implement how to update the view // You have to implement how to update the view addButton addButton. .addEventListener addEventListener( ('click' 'click', , ( () ) => => { { view view. .appendChild appendChild( (child child) ) } }) ); ;

Slide 11

Slide 11 text

Declarative const const view view = = document document. .querySelector querySelector( ('.view' '.view') ); ; const const addButton addButton = = document document. .querySelector querySelector( ('.add-button' '.add-button') ); ; const const state state = = [ [] ]; ; addButton addButton. .addEventListener addEventListener( ('click' 'click', , ( () ) => => { { // update the state impleratively // update the state impleratively state state. .push push( (child child) ); ; // describe the view declaratively based on the state // describe the view declaratively based on the state render render( (state state) ); ; } }) ); ; // describing what the view should display // describing what the view should display const const render render = = state state => => { { view view. .innerHTML innerHTML = = state state. .map map( (s s => => ` ` ${ ${s s} } ` `) ). .join join( ('' '') ); ; } } Describing what to update the view

Slide 12

Slide 12 text

= View(State)

Slide 13

Slide 13 text

React updates views efficiently let let count count = = 1 1; ; ReactDOM ReactDOM. .render render( ( <
> < > <

>{ {count count} }< /p p> > < /div div> >, , container container ) ); ; count count = = 2 2; ; ReactDOM ReactDOM. .render render( ( <

> < > <

>{ {count count} }< /p p> > < /div div> >, , container container ) ) // p.textContent = 2; // React updates the DOM // p.textContent = 2; // React updates the DOM

Slide 14

Slide 14 text

ReactDOM Renderer

Slide 15

Slide 15 text

Describing what the program must accomplish in terms of the problem domain

Slide 16

Slide 16 text

Abstract your application components DOM is an implementation detail React Component is a primitive of your domain.

Slide 17

Slide 17 text

Build own domain layers with React const const view view = = document document. .querySelector querySelector( ('.view' '.view') ); ; // describing what the view should display // describing what the view should display const const App App = = ( () ) => => { { const const [ [items items, , setItems setItems] ] = = useState useState( ([ [] ]) ); ; return return ( ( < > < >title title< /Header Header> > < > { {items items. .map map( (item item => => < >) )} } < /ItemList ItemList> > < => setItems setItems( (items items. .concat concat( (item item) )) )} } / /> > < /Layout Layout> > ) ); ; } } ReactDOM ReactDOM. .render render( (< >, , view view) ); ;

Slide 18

Slide 18 text

DOM as a Second-class Citizen Sebastian Markbåge - DOM as a Second-cl Sebastian Markbåge - DOM as a Second-cl… … 後で見る 後で見る 共有 共有 Sebastian Markbåge / React Europe 2015

Slide 19

Slide 19 text

React Custom Renderer

Slide 20

Slide 20 text

Renderers

Slide 21

Slide 21 text

Ink import import React React from from 'react' 'react'; ; import import { {render render, , Box Box, , Color Color} } from from 'ink' 'ink'; ; render render( ( < > < >Hello world Hello world! !< /Color Color> > < /Box Box> > ) ); ;

Slide 22

Slide 22 text

ReactKonva ReactKonva ReactKonva. .render render( ( < > < > < > < > < /Layer Layer> > < /Stage Stage> >, , el el ) ); ;

Slide 23

Slide 23 text

ReactThreeFiber import import React React, , { { useRef useRef } } from from 'react' 'react' import import ReactDOM ReactDOM from from 'react-dom' 'react-dom' import import { { Canvas Canvas, , useFrame useFrame } } from from 'react-three-fiber' 'react-three-fiber' const const Cube Cube = = ( () ) => => { { const const ref ref = = useRef useRef( () ) useFrame useFrame( (( () ) => => ( (ref ref. .current current. .rotation rotation. .x x = = ref ref. .current current. .rotation rotation. .y y += += 0.01 0.01) )) ) return return ( ( < > < > < > < /mesh mesh> > ) ) } } ReactDOM ReactDOM. .render render( (< >< >< /Canvas Canvas> >, , el el) ); ;

Slide 24

Slide 24 text

ReactAST import import React React from from 'react' 'react'; ; import import { { renderAst renderAst, , Code Code, , ClassDeclaration ClassDeclaration, , FunctionDeclaration FunctionDeclaration } } from from 'react-ast' 'react-ast'; ; const const ast ast = = renderAst renderAst( ( < > < >const const hello hello = = 'world' 'world'< /Code Code> > < > < >return return 'bar' 'bar'< /Code Code> > < /FunctionDeclaration FunctionDeclaration> > < /ClassDeclaration ClassDeclaration> > ) ); ; console console. .log log( (ast ast) ); ;

Slide 25

Slide 25 text

Building a Custom React DOM Renderer https://github.com/jquense/react-dom-lite https://conf.reactjs.org/event.html?sophiebits

Slide 26

Slide 26 text

Architecture of React https://speakerdeck.com/koba04/algorithms-in-react

Slide 27

Slide 27 text

react-reconciler npm install react-reconciler npm install react-reconciler packages/react-reconciler

Slide 28

Slide 28 text

How to use import import Reconciler Reconciler from from "react-reconciler" "react-reconciler"; ; const const renderer renderer = = Reconciler Reconciler( (hostconfig hostconfig) ); ; export export const const YourReact YourReact = = { { render render( ( element element: : React React. .ReactNode ReactNode, , rootContainer rootContainer: : RootContainer RootContainer, , callback callback = = ( () ) => => { {} } ) ) { { if if ( (! !rootContainer rootContainer. .container container) ) { { rootContainer rootContainer. .container container = = { {} } rootContainer rootContainer. .container container. .fiberRoot fiberRoot = = renderer renderer. .createContainer createContainer( ( container container, , false false, , false false ) ); ; } } renderer renderer. .updateContainer updateContainer( (element element, , container container. .fiberRoot fiberRoot, , null null, , callback callback) ); ; } } } }

Slide 29

Slide 29 text

HostConfig Interface #1 Mutation(optional) getPublicInstance, getRootHostContext, getChildHostContext, prepareForCommit, resetAfterCommit, createInstance, appendInitialChild, finalizeInitialChildren, prepareUpdate, shouldSetTextContent, shouldDeprioritizeSubtree, createTextInstance scheduleDeferredCallback, cancelDeferredCallback, setTimeout, clearTimeout, noTimeout, now, isPrimaryRenderer supportsMutation, supportsPersistence, supportsHydration appendChild, appendChildToContainer, commitTextUpdate, commitMount, commitUpdate, insertBefore, insertInContainerBefore, removeChild, removeChildFromContainer, resetTextContent

Slide 30

Slide 30 text

HostConfig Interface #2 Persistence(optional) Hydration(optional) from @types/react-reconciler cloneInstance, createContainerChildSet, appendChildToContainerChildSet, finalizeContainerChildren, replaceContainerChildren canHydrateInstance, canHydrateTextInstance, getNextHydratableSibling, getFirstHydratableChild, hydrateInstance hydrateTextInstance,didNotMatchHydratedContainerTextInstance, didNotMatchHydratedTextInstance, didNotHydrateContainerInstance, didNotHydrateInstance,didNotFindHydratableContainerInstance, didNotFindHydratableContainerTextInstance, didNotFindHydratableInstance, didNotFindHydratableTextInstance

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

HostConfig of renderers ReactDOM packages/react-dom/src/client/ReactDOMHostConfig.js ReactNative packages/react-native-renderer/src/ReactNativeHostConfig.js packages/react-native-renderer/src/ReactFabricHostConfig.js ReactTestRenderer packages/react-test-renderer/src/ReactTestHostConfig.js Ink vadimdemedes/ink/blob/master/src/reconciler.js ReactKonva konvajs/react-konva/blob/master/src/ReactKonvaHostConfig.js

Slide 33

Slide 33 text

HostConfig? Side effects for a Host environment Define instances Define the mode for a renderer Hydration logic (if you need)

Slide 34

Slide 34 text

Side effects for a Host environment ReactDOM ReactDOM. .render render( ( <
    > <
  • >a a< /li li> > <
  • >b b< /li li> > <
  • >c c< /li li> > < /ul ul> >, , container container ) ); ; ReactDOM ReactDOM. .render render( ( <
      > <
    • >b b< /li li> > <
    • >a a< /li li> > <
    • >c c< /li li> > < /ul ul> >, , container container ) ) // React update the DOM like the following // React update the DOM like the following // li.insertBefore(b, a); // li.insertBefore(b, a);

Slide 35

Slide 35 text

Side effects for a Host environment export export function function insertBefore insertBefore( ( parentInstance parentInstance: : Instance Instance, , child child: : Instance Instance | | TextInstance TextInstance, , beforeChild beforeChild: : Instance Instance | | TextInstance TextInstance ) ): : void void { { // we have to remove a current instance at first // we have to remove a current instance at first const const index index = = parentInstance parentInstance. .children children. .indexOf indexOf( (child child) ); ; if if ( (index index !== !== - -1 1) ) { { parentInstance parentInstance. .children children. .splice splice( (index index, , 1 1) ); ; } } // And then, we insert the instance into a new index // And then, we insert the instance into a new index const const beforeIndex beforeIndex = = parentInstance parentInstance. .children children. .indexOf indexOf( (beforeChild beforeChild) ); ; parentInstance parentInstance. .children children. .splice splice( (beforeIndex beforeIndex, , 0 0, , child child) ); ; } }

Slide 36

Slide 36 text

Others appendChild, appendInitialChild, appendChildToContainer commitTextUpdate, commitMount, commitUpdate insertBefore, insertInContainerBefore removeChild, removeChildFromContainer, resetTextContent

Slide 37

Slide 37 text

createInstance, createTextInstance export export function function createInstance createInstance( ( type type: : Type Type, , props props: : Props Props, , rootContainerInstance rootContainerInstance: : Container Container, , hostContext hostContext: : HostContext HostContext, , internalInstanceHandle internalInstanceHandle: : OpaqueHandle OpaqueHandle ) ): : Instance Instance { { return return createYourHostInstance createYourHostInstance( (type type, , props props) ); ; } } export export function function createTextInstance createTextInstance( ( text text: : string string, , rootContainerInstance rootContainerInstance: : Container Container, , hostContext hostContext: : HostContext HostContext, , internalInstanceHandle internalInstanceHandle: : OpaqueHandle OpaqueHandle ) ): : TextInstance TextInstance { { return return createYourTextInstacne createYourTextInstacne( (text text) ); ; } }

Slide 38

Slide 38 text

getPublicInstance export export function function getPublicInstance getPublicInstance( ( instance instance: : Instance Instance ) ): : PublicInstance PublicInstance { { return return convertToPublicInstance convertToPublicInstance( (instance instance) ); ; // react-dom // react-dom // return instance; // return instance; } }

Slide 39

Slide 39 text

Define the mode for a renderer export export const const isPrimaryRenderer isPrimaryRenderer = = true true; ; export export const const supportsMutation supportsMutation = = true true; ; export export const const supportsPersistence supportsPersistence = = false false; ; export export const const supportsHydration supportsHydration = = false false; ;

Slide 40

Slide 40 text

Type Definition for custom host config declare namespace declare namespace JSX JSX { { interface interface IntrinsicElements IntrinsicElements { { text text: : { { color color: : string string; ; children children? ?: : React React. .ReactNode ReactNode; ; } }; ; } } } } https://www.typescriptlang.org/docs/handbook/jsx.html#intrinsic-elements

Slide 41

Slide 41 text

Live Coding

Slide 42

Slide 42 text

Thank you!!! speakerdeck.com/koba04/ github.com/koba04/jsconf-jp-presentation