Iโve been working with React Native for over a year now. And one thing that is driven home by my UX team is the need to provide feedback to the user that something is happening. What Iโve found is that while the React Native <Image/>
component provides us with the ability to have a placeholder via the defaultSource property, this can lead to a less than optimal user experience if not implemented properly.
NEW RESEARCH: LEARN HOW DECISION-MAKERS ARE PRIORITIZING DIGITAL INITIATIVES IN 2024.
Whether itโs within a ListView implementation or what is known as a Detail View, rendering images within mobile applications requires some finesse, given the UX demands of finely developed mobile applications. Web developers transitioning to React Native need to pay particular attention to how they render images in their application, where the โweb wayโ of rendering images just wonโt fly. For example, images rendering as they download is not something that is good UX for mobile applications.
Below is a gif of the YouTube homepage on a desktop loading on a slow network.
We can see that there are grey boxes as placeholders in the images above. Weโre going to replicate that behavior in this post, as we take a deep dive into a pattern that you can leverage to easily add a visually pleasing pre-loading placeholder to your applicationโs Images.
What weโre building
Weโll create a very simple application that displays a two-column grid of Images. Weโll be using a plain <ScrollView/>
over a <ListView/>
component because a <ListView/>
would simply make things more complicated than necessary to demonstrate this pattern, though it is advised to use a <ListView/>
for this pattern for optimal performance. The goal of this post is to drive home the need for a multi-phase approach for rendering with placeholder images.
As youโll soon learn, the code for this application is relatively simple. But in case youโre new to using React JS and React Native, Iโll explain all of it.
If youโre like me, you might want to see this running right away. Iโve setup a github repo here.
If youโre new to the React Native ecosystem, it might be prudent to review the code structure first. Otherwise, feel free to jump to the next section.
Contents of index.ios.js
import React, { // All necessary imports } from 'react-native'; // We'll place some constants here // Define needed styles const styles = StyleSheet.create({ }); class Placeholder extends Component { // Initial state state = { }; // Step #4 (Self-bound function) // We will update the state of the application so that images can render onAfterLoad = (data) => { }; // Step #1 & #2 // Here weโll fetch JSON for images to be displayed componentWillMount() { } // This will be responsible for rendering the image components buildImages(data) { } // Step #3 and #5 // Render the main view and child images render() { } }
How to setup image placeholders
One of the best ways to envision how this will work is thinking about a timeline.
The steps break down like this:
componentWillMount()
executes. This gives us some intelligence that our component will come to life, so we kick off the load, which is the next step.- Inside the body of
componentWillMount()
, weโll initiate afetch()
for the JSON data. Keep in mind that these requests are asynchronous, making the next step rather crucial for two reasons. First, we have no idea how long itโs going to take for thefetch()
to finish. Second, if we donโt show something meaningful, the user wonโt get the rapid visual feedback that they have become accustomed to with high-performing mobile applications. Without that feedback, they will get the wrong impression of the application’s ability to function and may elect to close it. - In this step, the
render()
phase of the component lifecycle kicks in. What you must keep in mind is that we donโt have data to display just yet, so weโll have to show the user something. We could display a loading indicator, but for some application designs, that pattern wonโt work. So weโre going to render enough placeholders to fill the screen. - Assuming step #2 completes successfully, the
fetch()
success callback handler will trigger a new state, passing in the newly fetched data. For simplicityโs sake, I did not put much error handling here. - This step calls
this.setState()
with an updated state object containing the image objects as a member labeled data, forcing a refresh of the render cycle. - Now that we have data in our state object, weโll render
<Image/>
components with both the placeholder image and the actual source URI will point to the images the user will want to see.
Here is a video that demonstrates what rendering of the view looks like with and without the placeholder images. On the left, youโll find a version of the application that only renders the <Image/>
components after the fetch has successfully completed, versus the two-pass approach described above.
Here are some things to think about as you review the code below.
- Youโll find that the
buildImages()
method, which is called byrender()
, is responsible for iterating over the data and producing instances of<Image/>
. It determines if we have data in the local state object (line #92) and will pad the array with ten undefines. - When the
fetch()
completes, weโll enter the second phase of the render cycle, where we expect to have data with URIs for the images we expect to render.buildImages()
will at that point use the data to configure the images to the proper URLs. - The last thing to think about is that we must set the
defaultSource
prop for the<Image/>
component to the placeholder. This allows the<Image/>
components to fetch the image assets, while allowing to display something for the user. - To exacerbate the situation, Iโm using Math.random() as a cache buster and am simulating 3G network conditions with information from this blog post. Afterall, slow network is exactly why weโre doing all of this work.
There you have it. The multi-phase approach to rendering images with placeholders while we wait for data to come in is not a difficult approach to configure in our applications.
Conclusion
In this article, we explored using a multi-phase render approach to a view that renders two columns of images using placeholders to assist with the user experience. In the next post, Iโll work to tackle this approach coupled with animating the fade-in of the images.
Thank you for reading! Has this article helped you? Please share it via social media channels.
Have questions or any suggestions for improvement? Post them below.
Jay Garcia
Related Posts
-
React Navigation and Redux in React Native Applications
In React Native, the question of โhow am I going to navigate from one screen…
-
Using ES2016 Decorators in React Native
*picture courtesy of pixabayDecorators are a popular bit of functionality currently in Stage 1 of…