React Native, as seen on TV

Rate this content
Bookmark

You might have heard of React Native's Many Platforms vision, but what about TVs? Find out about the joys and challenges of developing for 7 platforms at once with a single codebase!

20 min
21 Jun, 2022

Video Summary and Transcription

This Talk discusses TV app development using React Native, focusing on limitations and specific considerations. The speaker demonstrates how to fetch and display random dog images using React Native hooks. They also explain how to handle focus and spatial navigation in TV apps, including using focus keys and a spatial navigation library. The Talk emphasizes the importance of optimization and performance in TV app development and suggests using useMemo and useCallback to avoid unnecessary re-renders. The speaker concludes by mentioning job opportunities at JustWatch.

1. Introduction to TV Apps Development

Short description:

In this part, Michele introduces Just Watch, a TV guide for streaming services, and shares their experience in finding information about TV apps development. They discuss the limitations and specific considerations for building TV apps using React Native, including directional navigation and the use of React Native tvOS and React Native web for different platforms.

Hi there, welcome to React Native as seen on TV. I'm Michele, I'm a software developer at Just Watch. What is Just Watch in case you don't know? It's the TV guide for streaming services. So if you don't know where your favorite movie or TV show is available, or if you want to get say a notification the next time a new season for your favorite TV show comes out, you can head over to our website or use one of our many apps, including not only iOS and Android, but also TVs.

So when I joined Just Watch, I didn't know much about TV apps development, and I honestly found it kind of hard to find information about it online. Because yes, there is a section that's dedicated to it on the React Native website, but there isn't that much. So last year, when the React team came up with this blog post titled the Many Platform Vision, I was all excited and thought, oh, nice, they're finally talking about TV apps development. But it turns out that they weren't. They were really talking about React Native Windows and Mac OS and VR, but no mention of TVs. So this thing here in the animation is not a TV in case you thought that, it's actually a desktop. So bummer.

So in my talk today, I'm going to talk to you about how you can build your TV apps using React Native. What's special about TV apps development compared to mobile app development. And finally, I'm going to give you a couple of small tips and tricks that you can use to make your app perform better in a constrained environment such as the TV environment. So when you're building for TVs, the three main platforms that you're going to target are Android TV and Fire TV, based on Android, and tvOS. For these, you can use React Native tvOS, which is a fork of the main React Native repo, which is kind of unfortunate because it means that it's always going to be a little behind, but it's being updated fairly regularly. And this React Native tvOS repo gives you a number of functionality and features that you can use to build your TV apps with. This is not the whole picture though, because if you want to target LG TVs and Samsung TVs or Xboxes like we do, you will need to build your app as an HTML app. And to do so while still using the same code base that you're using for the other platforms, you can use React Native web. So talking about things that are specific to TV apps development, there is definitely directional navigation or arrow key based navigation. Your users will interact with your app only using their remote. So their options are limited there. They can only go up, down, left, right, select, go back, unless they have an LG TV, which has its own magic remote. It's really called like that, which is a pointer based interface. But other than that, they will have this arrow key based navigation. React Native tvOS gives you this TV event handler that lets you intercept these key events. And it also gives you a number of props like focusable, unfocus, unblur. These work exactly like they do on pressable on the regular React Native repo. But also some that are specific to TV app development, such as hasTVPreferredFocus, which tells the OS which component should get focused by default when rendering your view. Or the next focus down, left, up, and right props.

2. TV App Development with React Native

Short description:

In this part, Michele discusses the complexity of focus in TV app development compared to mobile app development. They demonstrate a React Native app running on the Apple TV simulator and explain how to clear the code to develop TV apps with React Native. Michele also introduces a public API called Docs.io and explains how to fetch and display random dog images using React Native hooks.

And these are also used to tell the OS where focus should go, in this case, when users press the arrow keys. So compared to mobile app development, focus is a lot more complex. Usually with mobile apps, what you do is simply say, hey, if this input field is focused, please bring up the keyboard. Something like that. Not much more than that, usually.

So let's look at some code. I have a repo here, which I created using the React Native CLI, cloning this template, React Native template TypeScript TV. So if I launch Yarn iOS, this is going to bring up the Apple TV simulator and the regular React Native demo app. It looks a little weird with these colors. I'm not sure why. And aside from that, it's just your regular app. But you can see on the top right here, you can see that it's telling me when I'm pressing arrow keys, which one was pressed. This is React Native TV OS.

So let's clear everything inside here so I can show you how it is to develop TV apps with React Native. So I have my own styles, nothing fancy, just, yeah, they're my own. I'm importing them, and I'm clearing the imports. So now we have a blank canvas. Here we go. So I have this API, which is a public API called Docs.io. It's not mine. I'm just using it. I'm fetching 10 random images of dogs and giving them an ID based on their position in the array. So let's get them when the component is mounted. I'm using hooks. So dogs, set dogs. Let's have a useState, which is going to contain an array of images. We want to fetch it, so let's have a useEffect, which will actually fetch the dog images. Here we go. It's a promise. So when I get the result, I'm calling setDogs.

3. Rendering Dog Images and Handling Focus

Short description:

I want to run this only once on React Native 17. TypeScript doesn't know the type of array. The function fetchDogs returns a promise. Now I'm mapping over the dogs and rendering a list of dog images. I need to handle focus and keep track of the currently focused component.

And I want to run this only once, at least on React Native 17. And this is not good because TypeScript doesn't know what type of array this is. So this is going to be the return type of the function that I just called. So fetchDogs. Here we go.

Still not good because it's a promise and I want to get the result value instead. So now it's all good, but I'm still not rendering anything. So let's render it here. Let's map over our dogs. So now I have a dog here, dog image actually. And let's render a pressable, which is exactly the same pressable that you know and love from React Native. Here we go. Pressable. Let's give it a key because it's inside of an array. And let's render an image inside, which is the image of the dog, kind of what we want it. So URI dog image. Here we go. Let's give it a style. Style. An image. And let's say it needs to take the whole area available. Here we go. So now we should have, and we do, a list of images of dogs.

And if I'm holding down the Option key and scroll here, you can see that it actually scrolls. But if I press the arrow keys instead, you can see that it still scrolls, but nothing changes because I'm not rendering anything different if something is focused. So first of all, let me make it horizontal because I like it better. And let's keep track of focus, which is something that you will do all the time in TV apps. So let's hold the state here. And let's say that this is the ID of the component that's currently focused. The ID is just going to be a string.

4. Tracking Focus and Adding Buttons

Short description:

And let's keep track of the focused dog by updating the focus ID with a callback. If the dog is focused, update the style to include a blue frame. Add another button for 2D navigation. That's it!

And let's keep track of it. So whenever one of those dogs is focused, I want to update the focus ID. So how do I do that? With a callback. Let's call it on focus. And let's say use callback. I'm going to pass this ID to this function. And it's still going to be a callback. So I want another arrow here. And I want to call set focused ID, passing it the ID. Here we go. Array of dependencies. Boom.

On focus. And now let's pass it the ID of the dog. Still not changing anything render-wise. I want to update the style. So I have the styles pressable. But if it's focused, I want it to also have a frame, a blue frame around it, which is in a style that I have called styles focused. And so if this is the dog with the focus ID, so dog ID is equal to focus ID, I want to add this other style. So not only pressable, but also focused. So here we go. And now if I move around with the arrow key, you can see that everything works.

So let's add another button just below so that we have 2D navigation at least. So let's say styles.buttons. Yep. And let's add another pressable. And let's give it the same treatment that we gave the dogs. So if focused ID is equal to, let's call it button, I'm going to say styles.button, but also styles.focused. If not, only styles.button. What was that? I don't know.

5. Handling Focus and Spatial Navigation

Short description:

Styles.button. We need to update focus and render the label inside the button. To enable focus navigation, set the next focus down prop using refs and the findNodeHandle function. Ensure you have the latest version of React Native tvOS for bug fixes. For React Native web, use a spatial navigation library like Norwegian Spatial Navigation.

Styles.button. Here we go. We also need to update focus. So again, on focus, and this is button we just said. And inside of it, let's render just the label. Style label. Here we go. And let's just say that this is button. So this just adds another button here, and I can navigate to it.

But if I press down here, for example, or here on any dog image that's not the first one, tvOS doesn't let me focus the button. This is because tvOS by default doesn't let you move down with focus if the component that you want to focus is not directly below the one that's currently focused. So it works here, but it doesn't work here. So how do I tell tvOS otherwise? I tell it by setting the next focus down prop. This works based on refs. So I will have to create a ref, calling it button.ref. Let's set it to null so that TypeScript doesn't complain. And let's set it on the button. So right here, ref is equal to button.ref.

And you might think that all I need to do here is just say next focus down button.ref, which would make sense, but it doesn't work because it really wants a number and not a ref. So to get a number out of a ref, you need to use React Native's findNodeHandle function. FindNodeHandle. Here we go. So it takes, of course, a current value of some ref, and it actually doesn't let me set it like this because it cannot be null, but it can be undefined. So I can just say this. So now if I save and I load and I press down here, everything works. It doesn't matter where I am.

So be sure to have the latest version of React Native tvOS because there was a bug for tvOS concerning next focus down or next focus anything only on tvOS, but it got fixed in the latest version. We looked at this for React Native tvOS, but if you're in React Native web instead, there is no native focus management because it's just an HTML page. In that case, you will need to add your own spatial navigation library, such as Norwegian Spatial Navigation, which we're using. It used to be called React Spatial Navigation.

6. Using Focus Keys and Spatial Navigation

Short description:

This library is based on a unique focus key, which allows you to imperatively set focus on a specific component. You can define your own next focus props and create your own focus functions. To enable spatial navigation on React Native Web, you can use the Norwegian spatial navigation library. The useFocusable hook is essential for setting refs on focusable components. Extracting components for different platforms, such as web, is a common practice in TV app development.

It got recently renamed because they rewrote it to use hooks instead of high order components. This library is based on a unique focus key, which is a string, which is kind of better than a ref, I think, although you will need to deal with refs, but we'll see about that. And you can pass these unique focus IDs to focus keys to this set focus function, which is also provided by the library.

And that set focus function lets you imperatively set focus on a specific component, which is kind of nice. You can still define your own next focus props because those are specific to React Native tvOS, but you can create your own such as next focus down web, let's say, using the onArrowPressed function, which is provided by the library. So let's check it out.

So I have another branch here that I want to check out in which I installed React Native Web. So I have a webpack configuration. So if I run yarn web, this is going to bring up a webpack dev server. I have just a regular configuration for TypeScript. And if I look at localhost 8080, now it should be served. So focus kind of works if I press on things like I'm doing right now. But if I press the arrow keys, it doesn't do anything, as you can see, because there's no spatial navigation.

So here I did add Norwegian spatial navigation. I did the setup. There's this init function. You can pass it some values that I didn't. You need to wrap your app around this focus provider. And this focus key that I was mentioning before is actually passed by the useFocusable hook. This is the number one hook that you want to use from this library. You will use it everywhere. I was also telling you before that you will still need to deal with refs. And why? Because these useFocusable hooks will actually return a ref that you need to set on every component that can be focused by the library. So since you need to use a ref for each component, these need to be extracted to a different component, which I did in this doc component. As you can see, it's exactly the same. So something that you will do more than once, at least, while developing for TV apps is to create different flavors of components, depending on whether you're on web or not. So to do this, you do it like you do for iOS and Android. So I'm copying and pasting this class and renaming it doc.web.tsx. So this will be loaded by Webpack only and not by the regular React Native metro bundler. So let's use this doc component here.

7. Enhancing UI with Spatial Navigation

Short description:

And let's add all the missing attributes. When in the web version, Spatial Navigation deals with focus. The focused prop, returned by the useFocusable hook, needs to be set on the component that can be focused. Synchronize the UI state with React Spatial Navigation or an origin Spatial Navigation using the focusSelf function. Use useEffect to handle the hasToBePreferredFocus prop. Set the ID for the focus key using the useFocusable hook. Update the type definition everywhere for TypeScript understanding.

So just like this. And let's add all the missing attributes. So image is doc.image and its focus is actually not always false, but focusedId equal to docId. So what we want to do when we're in the web version is actually let Spatial Navigation deal with focus. So this attribute will be ignored. So even if it's going to be in the interface, we're not going to use it here.

We want to have the focused prop, which is returned by the useFocusable hook. And I was telling you that this is ref-based because this returns some refs that you need to set on your component that can be focused. So that's all you need to do. Here we said we're using focused. And another thing that we need to do here is to synchronize the state of the UI with the state of React Spatial Navigation or an origin Spatial Navigation. So to do so, there's this focusSelf function that you need to call whenever your component is focused. So this keeps those in sync.

There's still one thing that we haven't done here, which is to deal with hasToBePreferredFocus because that's also React Native tvOS specific prop. So let's do a useEffect because this is only affected on mount. So if it is focused, so if it should be focused, call focusSelf. That's all there is to it. And finally, another thing that we can do is to actually decide on the ID that's used for our focus key. And to do so, you can actually set it as a parameter to the useFocusable hook. So you can say focusKey. And if you have IDs, I suggest you use them because it makes it better when you're debugging. So you need, of course, to define it. It's not alphabetically sorted, but yeah, whatever. And so this is ID. And I need to pass it. But now something that's also happening a lot when you have different flavors is you need to update the type definition everywhere because then TypeScript understands it. You can usually just extract that to a common file. That's only for types. That's what I usually do. So that's it.

8. Setting Focus and TV Performance

Short description:

We need to set the component with focus by default. Extract the button to a component and add the useFocusable hook. Use the setFocus function to set focus imperatively. TVs have underpowered CPUs compared to mobile phones. TVs are not replaced as frequently as phones, so older TVs may have slower CPUs and older software versions.

And of course, now, since this is not native, we have to tell it, hey, this is the component that has focus by default. So the very first dog will have focus by default. So let's save everything. Let's see when it reloads. And now everything works. Pretty cool.

So now the button doesn't work because, again, this has not been extracted to a component and it doesn't have the hook. So to do so, we need to extract it. I have the same thing that I showed you for dogs, for a button, and that's all it does. It has its own focusable, useFocusable and ref.

So I told you about a setFocus function, and that's also returned by this useFocusable hook. We can, for example, just set focus imperatively when you press that button. So let's say that what we want to do is to onPress setFocus to the third dog, maybe. So let's say just to show something. So let's say setFocus dog2. And we need to add it. Here we go. And now onPress will do that. So onPress. So saving everything again. Here we go. I'm using the arrow keys. And if I press the button, you can see that focus goes to the third dog as we want it.

So final considerations. So TVs are very underpowered in general. Their CPUs are smaller, slower than the latest and greatest mobile phones. And that's also because they get replaced less often as phones, thankfully, because they're big devices. But usually people don't change their TV every other year like they do with phones. So if you're looking at a TV from 2017, that's not really old. And a high-end TV from 2017 might have a 1 GHz CPU and Chromium 38, which is actually from 2014.

9. Optimization and Focus in TV App Development

Short description:

TV app development requires optimization due to limitations and the need for performance. Using useMemo and useCallback is crucial to avoid unnecessary re-renders and maintain performance. A trick is to only re-render components when focus changes, using React memos second argument. This functionality is similar to shouldComponentUpdate in class components. Unfortunately, there isn't enough time to explain in-depth, but JustWatch is hiring and you can reach out on Twitter or the React Summit website.

And they don't get replaced ever in some cases. So you will need to use Babel a lot, add a lot of polyfills, so it will get slower. So bringing up things from the past, so 2017 TV, so 2005 Meme. So you'll have to optimize a lot.

So remember to use useMemo, useCallback, and measurePerformance. Why am I saying this? Because lately, there's been a push against useMemo and useCallback, saying that it's kind of premature optimization. I think it's kind of the opposite, at least for TV app development, because you don't know if they actually require a lot of CPU to use useMemo and useCallback. But you definitely know that if you forget to use these, and you don't have referential integrity, so if functions keep getting changed and keep triggering re-renders all the time, this will hit performance a lot.

A trick that you can pull on TV apps specifically is to only re-render a component when focus changes. What do I mean by this? So TV app components fairly frequently are very complex, and they have a lot of props and values that you pass to them. But usually, you only want to re-render them when they're either the component that's currently focused or the one that used to be focused. So maybe you have a frame like I had in my demo app, and you want to move it. I only need to re-render those two. So how do I do that? I can use React memos second argument, which is not a secret, but not many know about it. Although this is exactly the same functionality that's provided by shouldComponentUpdate in the old lifecycle class components. It's flipped. So instead of being shouldComponentUpdate, it's kind of shouldComponentStayTheSame. So you're checking the previous props and the next props. And if focus didn't change, you don't want to re-render.

So I wish I had more time to actually go more in-depth to explain you all these tricks. But unfortunately, yeah, my 20 minutes are over. So thanks a lot for watching. JustWatch is hiring, so please reach out to me on Twitter or on any of my contacts, which are published on the React Summit website.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2023React Advanced Conference 2023
29 min
Raising the Bar: Our Journey Making React Native a Preferred Choice
At Microsoft, we're committed to providing our teams with the best tools and technologies to build high-quality mobile applications. React Native has long been a preferred choice for its high performance and great user experience, but getting stakeholders on board can be a challenge. In this talk, we will share our journey of making React Native a preferred choice for stakeholders who prioritize ease of integration and developer experience. We'll discuss the specific strategies we used to achieve our goal and the results we achieved.
React Finland 2021React Finland 2021
27 min
Opensource Documentation—Tales from React and React Native
Documentation is often your community's first point of contact with your project and their daily companion at work. So why is documentation the last thing that gets done, and how can we do it better? This talk shares how important documentation is for React and React Native and how you can invest in or contribute to making your favourite project's docs to build a thriving community
React Day Berlin 2023React Day Berlin 2023
29 min
Bringing React Server Components to React Native
React Server Components are new topic in community, bunch of frameworks are implementing them, people are discussing around this topic. But what if we could use React Server Components in React Native? And bring all optimisation features that RSC allows to mobile apps? In this talk I would present what we are able to do with RSC in React Native!
React Advanced Conference 2021React Advanced Conference 2021
21 min
Building Cross-Platform Component Libraries for Web and Native with React
Top Content
Building products for multiple platforms such as web and mobile often requires separate code-based despite most of the components being identical in look and feel. Is there a way where we could use shared React component library on different platforms and save time? In this presentation I'll demonstrate one way to build truly cross-platform component library with a unique approach of using React & React Native in combination.

Workshops on related topic

React Summit 2022React Summit 2022
117 min
Detox 101: How to write stable end-to-end tests for your React Native application
Top Content
WorkshopFree
Compared to unit testing, end-to-end testing aims to interact with your application just like a real user. And as we all know it can be pretty challenging. Especially when we talk about Mobile applications.
Tests rely on many conditions and are considered to be slow and flaky. On the other hand - end-to-end tests can give the greatest confidence that your app is working. And if done right - can become an amazing tool for boosting developer velocity.
Detox is a gray-box end-to-end testing framework for mobile apps. Developed by Wix to solve the problem of slowness and flakiness and used by React Native itself as its E2E testing tool.
Join me on this workshop to learn how to make your mobile end-to-end tests with Detox rock.
Prerequisites- iOS/Android: MacOS Catalina or newer- Android only: Linux- Install before the workshop
React Advanced Conference 2022React Advanced Conference 2022
81 min
Introducing FlashList: Let's build a performant React Native list all together
Top Content
WorkshopFree
In this workshop you’ll learn why we created FlashList at Shopify and how you can use it in your code today. We will show you how to take a list that is not performant in FlatList and make it performant using FlashList with minimum effort. We will use tools like Flipper, our own benchmarking code, and teach you how the FlashList API can cover more complex use cases and still keep a top-notch performance.You will know:- Quick presentation about what FlashList, why we built, etc.- Migrating from FlatList to FlashList- Teaching how to write a performant list- Utilizing the tools provided by FlashList library (mainly the useBenchmark hook)- Using the Flipper plugins (flame graph, our lists profiler, UI & JS FPS profiler, etc.)- Optimizing performance of FlashList by using more advanced props like `getType`- 5-6 sample tasks where we’ll uncover and fix issues together- Q&A with Shopify team
React Summit Remote Edition 2021React Summit Remote Edition 2021
60 min
How to Build an Interactive “Wheel of Fortune” Animation with React Native
Top Content
Workshop
- Intro - Cleo & our mission- What we want to build, how it fits into our product & purpose, run through designs- Getting started with environment set up & “hello world”- Intro to React Native Animation- Step 1: Spinning the wheel on a button press- Step 2: Dragging the wheel to give it velocity- Step 3: Adding friction to the wheel to slow it down- Step 4 (stretch): Adding haptics for an immersive feel
React Advanced Conference 2023React Advanced Conference 2023
159 min
Effective Detox Testing
Workshop
So you’ve gotten Detox set up to test your React Native application. Good work! But you aren’t done yet: there are still a lot of questions you need to answer. How many tests do you write? When and where do you run them? How do you ensure there is test data available? What do you do about parts of your app that use mobile APIs that are difficult to automate? You could sink a lot of effort into these things—is the payoff worth it?
In this three-hour workshop we’ll address these questions by discussing how to integrate Detox into your development workflow. You’ll walk away with the skills and information you need to make Detox testing a natural and productive part of day-to-day development.
Table of contents:
- Deciding what to test with Detox vs React Native Testing Library vs manual testing- Setting up a fake API layer for testing- Getting Detox running on CI on GitHub Actions for free- Deciding how much of your app to test with Detox: a sliding scale- Fitting Detox into you local development workflow
Prerequisites
- Familiarity with building applications with React Native- Basic experience with Detox- Machine setup: a working React Native CLI development environment including either Xcode or Android Studio
React Summit 2023React Summit 2023
88 min
Deploying React Native Apps in the Cloud
WorkshopFree
Deploying React Native apps manually on a local machine can be complex. The differences between Android and iOS require developers to use specific tools and processes for each platform, including hardware requirements for iOS. Manual deployments also make it difficult to manage signing credentials, environment configurations, track releases, and to collaborate as a team.
Appflow is the cloud mobile DevOps platform built by Ionic. Using a service like Appflow to build React Native apps not only provides access to powerful computing resources, it can simplify the deployment process by providing a centralized environment for managing and distributing your app to multiple platforms. This can save time and resources, enable collaboration, as well as improve the overall reliability and scalability of an app.
In this workshop, you’ll deploy a React Native application for delivery to Android and iOS test devices using Appflow. You’ll also learn the steps for publishing to Google Play and Apple App Stores. No previous experience with deploying native applications is required, and you’ll come away with a deeper understanding of the mobile deployment process and best practices for how to use a cloud mobile DevOps platform to ship quickly at scale.
React Advanced Conference 2022React Advanced Conference 2022
131 min
Introduction to React Native Testing Library
Workshop
Are you satisfied with your test suites? If you said no, you’re not alone—most developers aren’t. And testing in React Native is harder than on most platforms. How can you write JavaScript tests when the JS and native code are so intertwined? And what in the world are you supposed to do about that persistent act() warning? Faced with these challenges, some teams are never able to make any progress testing their React Native app, and others end up with tests that don’t seem to help and only take extra time to maintain.
But it doesn’t have to be this way. React Native Testing Library (RNTL) is a great library for component testing, and with the right mental model you can use it to implement tests that are low-cost and high-value. In this three-hour workshop you’ll learn the tools, techniques, and principles you need to implement tests that will help you ship your React Native app with confidence. You’ll walk away with a clear vision for the goal of your component tests and with techniques that will help you address any obstacle that gets in the way of that goal.you will know:- The different kinds React Native tests, and where component tests fit in- A mental model for thinking about the inputs and outputs of the components you test- Options for selecting text, image, and native code elements to verify and interact with them- The value of mocks and why they shouldn’t be avoided- The challenges with asynchrony in RNTL tests and how to handle them- Options for handling native functions and components in your JavaScript tests
Prerequisites:- Familiarity with building applications with React Native- Basic experience writing automated tests with Jest or another unit testing framework- You do not need any experience with React Native Testing Library- Machine setup: Node 16.x or 18.x, Yarn, be able to successfully create and run a new Expo app following the instructions on https://docs.expo.dev/get-started/create-a-new-app/