Bringing the best of React to TV navigation


A React-Like Solution for Remote Control


About us


Pierre Poupin
github.com/pierpo


Mathieu Fedrigo
twitter.com/mat_fdg

Context: Smart TVs




A simple TV App














Our goal




Our goal



Android codebase
iOS codebase
Web codebase
Our goal



React multiplatform codebase
Thanks React Native! Classic!
Our goal

press "right"
But... How do I handle spatial navigation with React Native?

How do I handle spatial navigation?
Native TV OS components?
❌ Not multiplatform...
❌ Not available on the web!
Custom navigation system in React?
✅ Multiplatform
🤯 but it's a hard problem!
Why is it hard?
rows, grids, nested grids, an element disappears, the focused element disappears, a modal appears...
Many edge cases of "where am I when [something] happens?"
- The expressive API we should have
- How we handled all this logic
- How we plugged this all to React!
We will cover:
Keeping track of the positions of the elements in React
🗺️
🧭
A hard problem with many solutions
- We had a very imperative logic 🪖
- Manual spatial registering of components
- ...in duplication of the React JSX declarations
- Which lead to a very leaky abstraction 🚰
First iteration: we faced dead-ends!
A hard problem with many solutions
- Result of the first iteration:
- 🪲 Lots of bugs
- 🐰 No dynamic updates (add/remove elements)
- 🌀 Layout could not be too deeply nested
First iteration: we faced dead-ends!
What we want to do

The declarative API
The declarative API
Basic Program


The declarative API
Focusable Program


The declarative API
Vertical container


The declarative API
Horizontal container → Row


The declarative API
Whole page


The Spatial Navigation problem

Spatial Navigation rules
React
The Spatial Navigation problem
Spatial Navigation rules
React

externalize the navigation logic!

The Spatial Navigation problem
Is there a lib for that? 📚️
github.com/bbc/lrud

LRUD = left-right-up-down
How LRUD works

Represent a spatial layout with a data structure
It's UI agnostic! 🦴
// not the exact structure, but global idea
{
root: {
id: 'root-element',
orientation: 'vertical',
children: [
{ id: 'row1', orientation: 'horizontal', children: [
{id: 'program-1-1', isFocusable: true }
]},
{ id: 'row2', orientation: 'horizontal', children: [
{id: 'program-2-1', isFocusable: true }
]}
]
}
}Represent a spatial layout with a data structure
It's UI agnostic! 🦴
How LRUD works
LRUD's representation is actually a tree!
(just like React 😎 it's a match! ❤️🔥)


How LRUD works
LRUD: Summary

Register the root
lrud.registerElement('root', { direction: 'vertical' })
Register a row
lrud.registerElement('root', { direction: 'vertical' })
lrud.registerElement('row1', { parent: 'root', direction: 'horizontal' })
Register programs
lrud.registerElement('root', { direction: 'vertical' })
lrud.registerElement('row1', { parent: 'root', direction: 'horizontal' })
lrud.registerElement('program-1', { parent: 'row1', isFocusable: true })
lrud.registerElement('program-2', { parent: 'row1', isFocusable: true })
Press right!
lrud.registerElement('root', { direction: 'vertical' })
lrud.registerElement('row1', { parent: 'root', direction: 'horizontal' })
lrud.registerElement('program-1', { parent: 'row1', isFocusable: true })
lrud.registerElement('program-2', { parent: 'row1', isFocusable: true })
lrud.handleKeyEvent('right') // actually, any key to get first focus
lrud.registerElement('root', { direction: 'vertical' })
lrud.registerElement('row1', { parent: 'root', direction: 'horizontal' })
lrud.registerElement('program-1', { parent: 'row1', isFocusable: true })
lrud.registerElement('program-2', { parent: 'row1', isFocusable: true })
lrud.handleKeyEvent('right')
lrud.handleKeyEvent('right')
Press right (again)!
LRUD + React
a love story with many chapters
2 trees that need to live together
React
LRUD



LRUD setup

Empty node


Register Node

Basic Context






Basic Context

Nested Context


Nested Context



Nested Context
Give id to children

Get parentId

Does it work?

LRUD ERROR: Cannot find "row1"
Render order

Logs order:


Logs order:


Render order
Tree traversal is
depth-first, pre-order (NLR)
Node → left path → right path

Credit: skilled.dev

Logs order:

Render order
Logs order:
Render order
Node → left path → right path

Logs order:

Render order
Node → left path → right path

Logs order:

Render order
Node → left path → right path

Logs order:

Render order
Node → left path → right path

Logs order:

Render order
Node → left path → right path

Logs order:

Render order
Node → left path → right path

Logs order:

Render order
Node → left path → right path

Logs order:

Render order
Node → left path → right path

useEffect order

Logs order:


Logs order:


useEffect order
Tree traversal is
depth-first, post-order (LRN)
left path → right path → Node

Credit: skilled.dev

Logs order:

useEffect order
Logs order:
Render order
left path → right path → Node

Logs order:
Render order
left path → right path → Node

Logs order:
Render order
left path → right path → Node

Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Logs order:
Render order
left path → right path → Node


Recap
Order useEffect
Order render


LRUD ERROR: Cannot find "row1"
A custom useEffect-ish

Basic


Basic
⚠️ don't try this at home

Basic

Dependencies

Destructor

Unmount
Fix registration order


Does it work?
Nothing happens?

Focused state

Pass focused state


Pass focused state

*That's not it
- Handling the scroll
- Handle pages on top of each other
- Side menu that is common between pages
- Navigation in virtualized lists
- Refreshing virtualized lists with new data
- Handling the default focus
We had many other challenges to tackle
The React-way = ❤️
Adopting a React-friendly declarative API was such a huge productivity boost
Thank you!

Check out https://blog.bam.tech !
React + LRUD
By Pierre Poupin
React + LRUD
- 207