Slide 1

Slide 1 text

Writing
 Your Own React Renderer @raphamorims

Slide 2

Slide 2 text

@raphamorims

Slide 3

Slide 3 text

careers.godaddy.com

Slide 4

Slide 4 text

Sorry folks. I don’t have discounts to domain purchase

Slide 5

Slide 5 text

I’m from Rio.

Slide 6

Slide 6 text

Summary React for the win • Elements, Instances and Components • Reconciler • Custom Renderers • React-Reconciler

Slide 7

Slide 7 text

Element

Chandler?! Oh. My. God.

/* React.createElement( "p", { style: { color: "green" } }, "Chandler?! Oh. My. God." ); */

Slide 8

Slide 8 text

Element // React.createElement(PokemonCard, { number: 26 });

Slide 9

Slide 9 text

Element Plain object. Describe a component instance.
 It’s what you want to see on the screen.

Slide 10

Slide 10 text

Component class PokemonTrainer extends React.Component { render() { const { name, imageSrc } = this.props; return (

{ name }

); } }

Slide 11

Slide 11 text

Component function PokemonCard({ number = 0 }) { const pokemons = useContext(PokemonContext); const { name, imageSource } = pokemons[number]; return ( <>

{ name }

); }

Slide 12

Slide 12 text

Component Function or class with a render() method that inherits from React.Component.

Slide 13

Slide 13 text

Instance class PokemonTrainerPicture extends React.Component { constructor(props) { super(props); this.imageRef = React.createRef(); } render() { const { imageSrc } = this.props; return ( ...

Slide 14

Slide 14 text

Instance Is what you refer to as this in the component class you write. It is useful for storing local state and reacting to the lifecycle events.

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

Reconciliation.

Slide 17

Slide 17 text

React provides a declarative API so that you don’t have to worry about exactly what changes on every update.

Slide 18

Slide 18 text

React provides a declarative API so that you don’t have to worry about exactly what changes on every update. Reconciliation is responsible for doing that.

Slide 19

Slide 19 text

Reconciliation is the React’s “diffing” algorithm. It makes component updates more predictable. When a component's state changes, React has to calculate if it is necessary to update the instances.

Slide 20

Slide 20 text

https://reactjs.org/docs/reconciliation.html
Elements of Different Types

Slide 21

Slide 21 text

https://reactjs.org/docs/reconciliation.html
Whenever the root elements have different types, React will tear down the old tree and build the new tree from scratch. Elements of Different Types

Slide 22

Slide 22 text

https://reactjs.org/docs/reconciliation.html
Elements of The Same Type

Slide 23

Slide 23 text

https://reactjs.org/docs/reconciliation.html
Elements of The Same Type
By comparing these two elements, Reconciler specifies what is going to be changed. In this case, React-DOM knows how to only modify the className on the underlying DOM node.

Slide 24

Slide 24 text

Component Elements of The Same Type Noiceee Dude! The instance stays the same, so that state is maintained across renders.

Slide 25

Slide 25 text

Fiber Reconciler.

Slide 26

Slide 26 text

A fiber represents a unit of work.

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

so.. reconciliation: is the algorithm React uses to diff one tree with another to determine which parts need to be changed. update: A change in the data used to render a React app. Usually the result of setState. Eventually results in a re-render.

Slide 29

Slide 29 text

scheduling: the process of determining when work should be performed. work: Any computations that must be performed. Work is usually the result of an update (e.g. setState).

Slide 30

Slide 30 text

A fiber represents a unit of work. It means that fiber represents a computation triggered by a data change.

Slide 31

Slide 31 text

This allow us to: - pause work and come back to it later. - assign priority to different types of work. - reuse previously completed work. - abort work if it's no longer needed.

Slide 32

Slide 32 text

Custom Renderers

Slide 33

Slide 33 text

React Renderers understand React component- driven architecture and it should provide such a declarative and composable abstraction as React. "Learn Once, Write Anywhere"

Slide 34

Slide 34 text

github.com/chentsulin/awesome-react-renderer React DOM React Ape React TV React Titanium React Hardware React Native DOM React Native React Blessed React Gibbon React PDF Proton Native PDF Documents DOM Structure Netflix’s Internal renderer (WebGL) Canvas DOM Structure Terminal interface (using Blessed) Renderer for Appcelerator® Titanium™ SDK Build firmata-based hardware applications Port of React Native to the web Build native mobile apps* Build native desktop apps

Slide 35

Slide 35 text

React Blessed

Slide 36

Slide 36 text

React Blessed import React, {Component} from 'react'; import blessed from 'blessed'; import {render} from 'react-blessed'; // Rendering a simple centered box class App extends Component { render() { return ( Hello World! ); } } // Creating our screen const screen = blessed.screen({ autoPadding: true, smartCSR: true, title: 'react-blessed hello world' }); // Adding a way to quit the program screen.key(['escape', 'q', ‘C-c'], (ch, key) => { return process.exit(0); }); // Rendering the React app using our screen const component = render(, screen);

Slide 37

Slide 37 text

React Hardware import React from 'react'; import ReactHardware, {Led} from 'react-hardware'; const HIGH = 255; const LOW = 0; class Application extends React.Component { constructor(props) { super(props); this.state = {value: 0}; this._timer = null; } componentDidMount() { this._timer = setInterval(_ => ( this.setState(prevState => ({value: prevState.value === HIGH ? LOW : HIGH})) ), this.props.interval); } componentDidUnmount() { clearInterval(this._timer); this._timer = null; } render() { return ( ); } } var PORT = '/dev/tty.usbmodem1411'; ReactHardware.render(, PORT);

Slide 38

Slide 38 text

React PDF import React from 'react'; import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer'; // Create styles const styles = StyleSheet.create({ page: { flexDirection: 'row', backgroundColor: '#E4E4E4' }, section: { margin: 10, padding: 10, flexGrow: 1 } }); // Create Document Component const MyDocument = () => ( Section #1 Section #2 );

Slide 39

Slide 39 text

No content

Slide 40

Slide 40 text

How to Start?

Slide 41

Slide 41 text

https://www.npmjs.com/package/react-reconciler

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

Ok. Let’s build a custom renderer together? What about music? Thanks to Ken Wheeler I can't be that original as I thought :’(

Slide 44

Slide 44 text

What we want to build?

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

{
 type: "Song",
 props: {
 bpm: 100,
 }
 }

Slide 47

Slide 47 text


 
 {
 type: "Song",
 props: {
 bpm: 100,
 children: {
 type: "Note", duration: 25, ticks: 15, number: 100
 }
 }
 }

Slide 48

Slide 48 text

WebAPI?

Slide 49

Slide 49 text

const MyAwesomeWebAPILibrary = { createSong: config => { // ... return Song; }, createNote: (ticks, number, velocity, duration) => { // ... return Note; }, createMIDI: data => { // data: or base64 const MIDI = new Promise((resolve, reject) => { // ... resolve(MidiFile); }) return MIDI; } };

Slide 50

Slide 50 text

// set the custom types export const Song = 'Song'; export const Note = 'Note';

Slide 51

Slide 51 text

import Reconciler from 'react-reconciler'; const SongReconciler = Reconciler(hostConfig); We will create a reconciler instance using Reconciler which accepts a host config object.

Slide 52

Slide 52 text

In this object we will define some methods which can be thought of as lifecycle of a renderer (update, append children, remove children, commit).

Slide 53

Slide 53 text

createInstance, appendInitialChild, createTextInstance, finalizeInitialChildren, getPublicInstance, prepareForCommit, prepareUpdate, resetAfterCommit, resetTextContent, getRootHostContext, getChildHostContext, scheduleAnimationCallback, scheduleDeferredCallback, useSyncScheduling, now, shouldSetTextContent, mutation […]

Slide 54

Slide 54 text

createInstance, appendInitialChild, createTextInstance, finalizeInitialChildren, getPublicInstance, prepareForCommit, prepareUpdate, resetAfterCommit, resetTextContent, getRootHostContext, getChildHostContext, scheduleAnimationCallback, scheduleDeferredCallback, useSyncScheduling, now, shouldSetTextContent, mutation […] Also, I challenge Ken Wheeler to remix this into a song.

Slide 55

Slide 55 text

const SongReconciler = Reconciler({ createInstance(type, props) {}, // e.g. DOM renderer returns a DOM node supportsMutation: true, // it works by mutating nodes mutation: {}, // mutation operations appendChild(parent, child) { // e.g. DOM renderer would call .appendChild() here }, … });

Slide 56

Slide 56 text

class Song { constructor({ metronome, bpm, bars}) { this.notes = []; this.config = { metronome, bpm, bars, } } addNote(note) { this.notes = this.notes.concat(note); } render() { let { notes, config } = this; const { metronome, bpm, bars } = config; const song = MyAwesomeWebAPILibrary.createSong({ bars, bpm, notes, metronome }); song.play(); } }

Slide 57

Slide 57 text

function createNote(props) { const { ticks, number, duration, velocity } = props; const Note = MyAwesomeWebAPILibrary.createNote( ticks, number, duration, velocity ); return {type: "note", value: Note}; }

Slide 58

Slide 58 text

function createCustomInstance(type, props) { const COMPONENTS = { Song: () => new Song(props), Note: () => createNote(props) }; return COMPONENTS[type] ? COMPONENTS[type]() : console.warn("ins't a valid element type"); }

Slide 59

Slide 59 text

const SongReconciler = reconciler({ createInstance( type, // Song props, // {} rootContainerInstance, //
or any root target hostContext, // NoContext internalInstanceHandle ) { const customInstance = createCustomInstance( type, props ); return customInstance; }, ...

Slide 60

Slide 60 text

... appendInitialChild(parentInstance, child) { const { type } = child; if (type === 'note') { parentInstance.addNote(child.value); } }, ... appendInitialChild

Slide 61

Slide 61 text

... finalizeInitialChildren(element, type, props) { if (element && element.render) { element.render(); // Song.render() } }, ... finalizeInitialChildren

Slide 62

Slide 62 text

Song createInstance

Slide 63

Slide 63 text

Song createNote appendInitialChild createNote createNote createNote createNote createNote

Slide 64

Slide 64 text

Song Notes Notes Notes Notes Notes Notes finalizeInitialChildren

Slide 65

Slide 65 text

And this concept is universal. You can build anything using components abstraction.

Slide 66

Slide 66 text

Give a try!

Slide 67

Slide 67 text

github.com/raphamorim/react-ape

Slide 68

Slide 68 text

Arts: http://avazaki.tumblr.com/post/168544406673 http://worldoro.tumblr.com/image/171366085872 https://reactjs.org/blog/2015/12/18/react-components-elements-and-instances.html https://reactjs.org/blog/2014/10/14/introducing-react-elements.html https://reactjs.org/docs/glossary.html https://github.com/abudaan/heartbeat https://medium.com/@agent_hunt/hello-world-custom-react-renderer-9a95b7cd04bc https://www.npmjs.com/package/react-reconciler https://github.com/nitin42/Making-a-custom-React-renderer https://github.com/acdlite/react-fiber-architecture https://reactjs.org/docs/reconciliation.html References

Slide 69

Slide 69 text

Obrigado! @raphamorims Thanks!