1 of 43

Building custom renderers with React

@satya164

2 of 43

Hello

👋 My name is Satyajit Sahoo

💻 I work at Callstack

📢 Follow me @satya164

2

@satya164

3 of 43

Custom Renderer

An alternative target for rendering React components

3

@satya164

4 of 43

4

@satya164

React DOM

React Native

React

Browser

Mobile

5 of 43

Custom renderers in the wild

5

@satya164

6 of 43

6

@satya164

7 of 43

7

@satya164

8 of 43

8

@satya164

9 of 43

Why?

Same declarative, component based API to build everything

9

@satya164

10 of 43

Fiber

10

@satya164

11 of 43

What’s Fiber?

The new reconciliation algorithm in React 16, based on data structure of the same name used internally.

11

@satya164

12 of 43

Reconciliation

The algorithm used to diff one tree with another to determine which parts need to be changed

12

@satya164

13 of 43

The Canvas API

const canvas = document.getElementById('canvas');

const ctx = canvas.getContext('2d');

ctx.fillStyle = 'black';

ctx.fillRect(0, 0, 100, 100);

ctx.fillStyle = 'white';

ctx.font = '14px sans-serif';

ctx.fillText('Hello world', 0, 14);

13

@satya164

14 of 43

What will we build

const App = () => (

<rectangle style={{ height: 100, width: 100 }}>

Hello world!

</rectangle>

);

render(<App />, document.getElementById('canvas'));

14

@satya164

15 of 43

15

@satya164

16 of 43

Let’s build it

16

@satya164

17 of 43

17

@satya164

document

body

a

head

p

text

div

span

page

18 of 43

Building blocks

  • Container
  • Rectangle

18

@satya164

Container

Rectangle

Rectangle

Rectangle

Rectangle

19 of 43

Nodes and React elements

// a react element

const element = <div />

// underlying DOM node (HTMLDivElement)

const node = ReactDOM.findDOMNode(element)

19

@satya164

20 of 43

The Container class

// src/Container.js

class Container {

// list of children, e.g. instances of Rectangle

children: [],

// some methods to modify children array

appendChild, removeChild, insertBefore,

// method to redraw the canvas

invalidate,

}

20

@satya164

21 of 43

The Rectangle class

// src/Rectangle.js

class Rectangle {

// list of children, e.g. - strings

children: [],

// some methods to modify children array

appendInitialChild, appendChild, removeChild, insertBefore,

// method to update rectangle properties

replaceProps,

// method to draw the rectangle

draw,

}

21

@satya164

22 of 43

Public API

// src/index.js

export function render(element, canvas) {

const container = CanvasRenderer.createContainer(

new Container(canvas)

);

CanvasRenderer.updateContainer(element, container, null);

}

22

@satya164

23 of 43

CanvasRenderer

23

@satya164

24 of 43

react-reconciler

// src/CanvasRenderer.js

import Reconciler from 'react-reconciler';

const CanvasRenderer = Reconciler({

// hostConfig

});

24

@satya164

25 of 43

The hostConfig object

  • createInstance
  • createTextInstance
  • appendInitialChild
  • mutation
    • appendChildToContainer
    • appendChild
    • removeChildFromContainer
    • removeChild
    • insertInContainerBefore
    • insertBefore
    • commitUpdate

25

@satya164

26 of 43

26

@satya164

createInstance

createTextInstance

appendInitialChild

27 of 43

createInstance

// src/CanvasRenderer.js

createInstance(type, props) {

switch (type) {

case 'rectangle':

return new Rectangle(props);

default:

throw new Error(`Invalid component type: ${type}`);

}

}

27

@satya164

28 of 43

createTextInstance

// src/CanvasRenderer.js

createTextInstance(text) {

return text;

}

28

@satya164

29 of 43

appendInitialChild

// src/CanvasRenderer.js

appendInitialChild(parentInstance, child) {

parentInstance.appendInitialChild(child);

}

29

@satya164

30 of 43

mutation

30

@satya164

31 of 43

appendChildToContainer

Append a new child to the container instance

31

@satya164

container

0

1

2

3

new child

32 of 43

insertInContainerBefore

Prepend a new child before another in the container instance

32

@satya164

container

0

1

2

3

new child

33 of 43

removeChildFromContainer

Remove a child from the container instance

33

@satya164

container

0

1

2

4

3

34 of 43

appendChild

Append a new child to a rectangle instance

34

@satya164

rectangle

0

1

2

3

new text

35 of 43

insertBefore

Prepend a new child before another child in a rectangle instance

35

@satya164

rectangle

0

1

2

3

new text

36 of 43

removeChild

Remove a child from the a rectangle instance

36

@satya164

rectangle

0

1

2

4

3

37 of 43

commitUpdate

Update the properties of a rectangle instance

37

@satya164

old tree

new tree

commitUpdate

new props

38 of 43

It’s demo time

38

@satya164

39 of 43

Integration with devtools

// src/index.js

CanvasRenderer.injectIntoDevTools({

bundleType: 1, // 0 for PROD, 1 for DEV

version: '0.1.0', // version for the renderer

rendererPackageName: 'canvas-renderer', // package name

findHostInstanceByFiber: CanvasRenderer.findHostInstance, // host instance (root)

});

39

@satya164

40 of 43

40

@satya164

41 of 43

Final implementation

41

@satya164

42 of 43

Resources

42

@satya164

43 of 43

Thank you

Any questions?

43

@satya164