22

I have some data in IndexedDB, which can only be accessed asynchronously. I want to build a React.js UI using that data. The general idea is that I'll have multiple React components that load data from IndexedDB and display some UI based on that data, and the user will be able to switch between which component is currently displayed.

My concern is that I don't know how to elegantly accomplish this without some superfluous UI flickering. I can do my asynchronous data loading in componentDidMount and put the data in this.state, but then render will be called before it's finished, forcing me to either display nothing or display some placeholder data for a tiny fraction of a second while the data from IndexedDB is retrieved.

I'd rather have it not render until after my data from IndexedDB is loaded. I know it won't take long to load, and I'd rather the previous component continue to display while the new data loads, so there is just one flicker (old -> new) rather than two (old -> blank/placeholder -> new). This is more like how a normal web page works. When you click a link from one website to another, your browser doesn't instantly show a blank/placeholder screen while it waits for the server from the linked website to respond.

I'm thinking I could do my data loading outside of the React component before calling React.render and then passing it in via this.props. But that seems messy, because nesting components would become tricky and some of my components will be updating over time and pulling new data from IndexedDB, through the exact same code that initializes them. So it seems like an ideal use case for storing data in this.state because then I could update it within the component itself when I get a signal that new data is available. Initialization and updating would be as easy as calling a this.loadData() function that sets some values in this.state... but then I have the aforementioned extra flicker.

Does anyone have any better ideas? What's the canonical solution to this problem? Is it really to just have millisecond blank/placeholder flickers all over the place?

dumbmatter
  • 9,351
  • 7
  • 41
  • 80
  • Have you tried it? In practice, I haven't had many problems with this. React has some bits under the hood that tend to keep things in sync. If you really need to, have a loading view and hide the other views. Then toggle off loading and show the other views after the data has arrived. – Cymen Jun 24 '15 at 01:36
  • Oh, and you might consider looking at Flux. If you use one of the Flux implementations, you'd have a store that would have the data and the view would listen to and rerender on store change. Works out well. – Cymen Jun 24 '15 at 01:37
  • Then don't render anything until you have the first bit of data. That seems straight forward. – Cymen Jun 24 '15 at 01:38
  • I've tried it. There is a visible flicker for a fraction of a second before the async data from IndexedDB gets rendered. That bothers someone like me quite a bit :). I don't see how flux would make a difference. Flux doesn't magically turn async to sync. And if I don't render anything until data is loaded, then my code becomes ugly as described in my last paragraph. Maybe that is the way to go, but I'm new to React and it just doesn't feel like the "right way". – dumbmatter Jun 24 '15 at 01:40
  • It depends what you are rendering. For example, with tabular data, you can render the empty table and preset the row height and width. Have you noticed what Facebook does? They show a generic feed with placeholders and then render into those. It isn't perfect as the placeholders don't match the size of the feed items but it stops the "from 0 to 300px" kind of thing. Maybe you could have some sort of placeholder? – Cymen Jun 24 '15 at 02:16
  • I could have a placeholder, but I'd rather not because it's ugly and it requires extra work. For context, I'm porting a large app that uses a hacky React/Flux-like library I built on top of Knockout a while back. But I like React better, so I'm trying to port it. In my library, my equivalent to `getInitialState` will wait for a returned promise to resolve before rendering. Then I can easily render page switches with no flicker. Adding a flicker would make the UI worse than it is now, and I'd like to avoid that. – dumbmatter Jun 24 '15 at 02:38
  • One last thing -- in your render, you can do an `if ... else ...` and return an empty span when the state isn't present. Would that solve the problem? That is all I have -- totally understand your situation though and it makes sense. – Cymen Jun 24 '15 at 03:44
  • I could do that, but the flicker is still there as it goes previousComponent -> blank -> nextComponent rather than previousComponent -> nextComponent. Thanks for all your comments though! – dumbmatter Jun 24 '15 at 03:57

1 Answers1

7

From the comments it sounds like the behavior you have in the previous implementation (waiting to navigate until you have fetched the necessary data) is the desired goal. If that's the case, the best way to do this without having the flickering would be to use some external object to manage the state and pass the data as props when it has been fetched.

React Router has a pretty good solution where it has the willTransitionTo hook to fetch data for a given component before navigating. This has the added benefit of allowing you to easily catch errors if something goes wrong.

Updated with new link:

https://github.com/reactjs/react-router

Joe P
  • 444
  • 2
  • 5
  • 1
    It seems the new location of react-router is here: https://github.com/reactjs/react-router – John Mar 06 '16 at 02:56
  • I've just plugged in the React Router - it is an awesome solution. The training video they have with it on their site (https://reacttraining.com/react-router/) is one of the most useful I've used. – icc97 Mar 15 '17 at 08:05