render to dom
TRANSCRIPT
COMPLICATEDBAD ARCHITECTURE OPTIMISATIONS
Boris Dinkevich
- React evangelist - Cat aficionado - Co-Author of “The complete Redux book”
Co-Founder @ 500Tech
A WORD ON TREES
Our code today
const Todos = () => ( <ul> <li>Todo</li> </ul>);
De-JSX
const Todos = () => ( <ul> <li>Stuff</li> </ul>);
const Todos = () => ( React.createElement("ul", null, React.createElement("li", null, "Stuff" ), ); );
Chrome dev tools?
WHY REACT?
DOM is slowLets minimise writes…
Our Sample App
TodosHeader
TodoTodo
App
Tree & DOM
TodosHeader
“div”
“Welcome”
“h1”
“div”
Todo
“Two”
“li”
“ul”
Todo
“One”
“li”
“div”
“Welcome”
“h1”
“div”
“Two”
“li”
“ul”
“One”
“li”
Our tree Our DOM
App
Introducing BorisJS!1.Call render() to build tree - ”Future Tree”
2.Compare each element to DOM
3.Change when needed
4.Win!
React docs: “reads are slow”Lets open PR
Simple diff
function updateNode(node, value) { if (node.innerText !== value) { node.innerText = value; } }
60fpsTime per frame: 16.6ms
Time for 1000 read / write cycles: 36.15ms
Read & Write
Read then Write
Introducing BorisJS 2.01.Call render() to build tree
2.Read DOM into temp tree - “Current Tree" 3.Compare each element to temp tree
4.Change when needed
5.Win!
“temp tree”… sounds familiar
Pre cache1. Call render() to build “Future Tree”
2. Compare each element to “Current Tree“
3. Change DOM when needed
4.Save “Future Tree” as “Current Tree” 5. Win!
What about first “Current Tree”?
Introducing BorisJS 3.0Initial Render
1.Call render() to build tree
2.Save as “Current Tree” 3.Create initial DOM
Updates
1.Call render() to build “Future Tree”
2.Compare each element to “Current Tree” 4.Change DOM when needed
5.Save “Future Tree” as “Current Tree”
6.Win!
NO READS?
Todos.setState({ todos: [’Uno’, ‘Two’] });
Todos• One • Two
Todos• Uno • Two
todos = document.getElementById('app').children[0].children[1]; todos.removeChild(todos.childNodes[0]);
Todos.setState({ todos: [‘Uno’, ‘Dos’] });
Todos• Dos
Todos• One • Two
Todos• Two
I LiedGet <input /> values?
In React• Initial Render
• Updates
FIRST RUN
React.createElement(App);
Starting
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
“Welcome”
“h1”
“div”
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
“Welcome”
“h1”
“div”
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
“Welcome”
“h1”
“div”
“One”
“li”
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
“Welcome”
“h1”
“div”
“One”
“li”
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
“Welcome”
“h1”
“div”
“Two”
“li”
“One”
“li”
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
“Welcome”
“h1”
“div”
“Two”
“li”
“ul”
“One”
“li”
Mounting markup
function Todos()
function Header()
“div”
“Welcome”
“h1”
“div”
function Todo()
“Two”
“li”
“ul
function Todo()
“One”
“li”
function App()
“Welcome”
“h1”
“div”
“Two”
“li”
“ul”
“One”
“li”
Mounting markup
“div”
“div”
“Welcome”
“h1”
“div”
“Two”
“li”
“ul”
“One”
“li”
#app
markup
<body> <div id="app"></div></body>
render( React.createElement(App), document.getElementById('app') );
DOM IS READY!Or is it?
DOM and React
Event Handers
<Component onClick={} />
Connection
React
onClick
DOM
React DOM
Our Code
Different renderersDOM - onClick
Native - onPress
What does this mean?
<Header onClick={} />
<Header onPress={} />
What is “React”?
addons 1,334 isomorphic 3,428 shared 7,058
renderers/ art 641 dom 12,337 native 2,735 noop 192 shared 9,368
* lines of code in 15-stable
TREE UPDATE
What will cause React to render again?• setState()
• forceUpdate()
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“One”
“li”
“div”
“Two”
“li”
“ul”
“One”
“li”
DOMReact Tree prev Virtual DOM
Todos.setState({ todos: ['Uno', 'Dos'] });
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“One”
“li”
“div”
“Two”
“li”
“ul”
“One”
“li”
DOMReact Tree prev Virtual DOM
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“One”
“li”
“div”
“Two”
“li”
“ul”
“One”
“li”
DOMReact Tree prev Virtual DOM
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“One”
“li”
“div”
“Two”
“li”
“ul”
“One”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“One”
“li”
“div”
“Two”
“li”
“ul”
“One”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“One”
“li”
“div”
“Two”
“li”
“ul”
“One”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“One”
“li”
“div”
“Two”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“Uno”
“li”
“div”
“Two”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“Uno”
“li”
“div”
“Two”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“Uno”
“li”
“div”
“Two”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“Uno”
“li”
“div”
“Two”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Two”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
Flow1. setState({ todos: [‘Uno’, ‘Dos’] });
2. Todos updated and rendering starts
3. Todo1 needs update
4. DOM updated
5. Todo2 needs update
6. DOM updated
AGAIN?
Todos.setState({ todos: ['Uno', 'Dos'] });
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
“div”
“Dos”
“li”
“ul”
“Uno”
“li”
DOMReact Tree prev Virtual DOM
‘UNO’ ‘DOS’
LIFE CYCLE
function Todos()
“div”
function Todo()
{ title }
“li”
“ul”
function Todo()
{ title }
“li”
function App()
React Tree
componentWillMount componentDidMount componentWillUpdate componentDidUpdate componentWillUnmount …
MULTIPLE UPDATES
Multiple setState calls
Todos.setState({ todos: [‘Uno’, ‘Dos’] }); App.setState({ title: ‘Bye’ }); Todos.setState({ todos: [‘Raz’, ‘Dva’] });
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App()
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App()
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App()
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 0
1
1
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 0
1
1
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 1
1
1
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 1
2
1
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 1
2
2
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 1
2
2
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 1
3
2
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 1
3
3
How many render() ?
App()
Todos()
Todos()
Todos()
Todo()
App() 1
3
3
transactions
How do we group?
Todos.setState({ todos: [‘Uno’, ‘Dos’] }); App.setState({ title: ‘Bye’ }); Todos.setState({ todos: [‘Raz’, ‘Dva’] });
Callback
Before
After
Transaction
Callback
Before
After
• Prepare
• Calculate final state • Update DOM • Run Callbacks
Transaction
• multiple setState() • Build list of changes
Todos
dirtyComponents
Todos.setState({ todos: [‘Uno’, ‘Dos’] });
{ todos: [‘Uno’, ‘Dos’] }
App
dirtyComponents
Todos.setState({ todos: [‘Uno’, ‘Dos’] }); App.setState({ title: ‘Bye’ });
{ todos: [‘Uno’, ‘Dos’] }
Todos
{ title: ‘Bye’ }
dirtyComponents
Todos.setState({ todos: [‘Uno’, ‘Dos’] }); App.setState({ title: ‘Bye’ }); Todos.setState({ todos: [‘Raz’, ‘Dva’] });
{ todos: [‘Uno’, ‘Dos’] } { todos: [‘Raz’, ‘Dva’] }
Todos
{ title: ‘Bye’ }
App
Finalize Transaction
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
0
0
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
0
0
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
0
0
Update the state to it’s latest version
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
1
0
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
1
1
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
1
1
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
1
1
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
1
1
Update the state
How many render() ?
App()
Todos()
Todos()
Todo()
App() 1
1
1
How many render() ?
App()
Todos()
Todos()
Todo()
App() 1
1
1
New props from App?
How many render() ?
App()
Todos()
Todos()
Todo()
App() 1
2
1
How many render() ?
App()
Todos()
Todos()
Todo()
App() 1
2
2
WAIT A MINUTE…
SORT BY ORDERSet order as components are mounted
How many render() ?
App()
Todos()
Todos()
Todo()
App() 0
0
0
How many render() ?
App()
Todos()Todos()
Todo()
App() 0
0
0
How many render() ?
App()
Todos()Todos()
Todo()
App() 0
0
0
How many render() ?
App()
Todos()Todos()
Todo()
App() 0
0
0
Update the state
How many render() ?
App()
Todos()Todos()
Todo()
App() 1
0
0
How many render() ?
App()
Todos()Todos()
Todo()
App() 1
0
0
Update the state
How many render() ?
App()
Todos()Todos()
Todo()
App() 1
1
0
How many render() ?
App()
Todos()Todos()
Todo()
App() 1
1
1
How many render() ?
App()
Todos()Todos()
Todo()
App() 1
1
1
How many render() ?
App()
Todos()Todos()
Todo()
App() 1
1
1
Callback order in transaction
Todos.setState({ todos: [‘Uno’, ‘Dos’] }); App.setState({ title: ‘Bye’ }); Todos.setState({ todos: [‘Raz’, ‘Dva’] });
1. App setState 2. Todos setState 3. Todos setState
Callbacks order
Transactions In ReactPreserving input selection
Suppressing events during DOM work
Collecting “componentDidUpdate”
etc
FINAL WORDS
REACT IS COMPLICATED SO YOUR CODE DOES’T HAVE TO BE