Search code examples
react.rb

How to handle loading state with Reactive Record lazy loading


I am hoping that someone can point me to the best way to handle loading state in a top level Reactrb component with is lazy loading data through Reactive Record.

My gaol is for the top level component to display a wait state while its children components get the data they need to render and then render the whole component quickly. I don't want individual wait states with child components as this creates a 'jerky' experience when there is a lot of data to load.

If you consider this example:

class PostsWithCommentsList < React::Component::Base

  before_mount do
    state.posts! Post.all
  end

  def everything_loaded?
      #how do I know when all posts and all comments are loaded?
  end

  def render
     div do
        ul do
          state.posts.each do |post|
            li { post.title }
            ul do
              post.comments.each do |comment|
                li { comment.body }
              end
           end
         end
      end if everything_loaded?
    end
  end
end

How do I get the everything_loaded? method to check that all th Posts and all the Comments have been loaded so the component draws quickly and smoothly?

All help very welcome. Thank you


Solution

  • There is a feature of ReactiveRecord that isn't finished, but is at a point where it can be useful. It's called WhileLoading, and the goal was to be able to say something like:

    def render
      div do
        ul do
          state.posts.each do |post|
            li { post.title }
            ul do
              post.comments.each do |comment|
                li { comment.body }
              end
            end
          end
        end.while_loading do
          # Some kind of loading screen
        end
      end
    end
    

    Basically you would call that method on the end of the element you want to have have something shown while it is loading, and then pass in what you want shown. Under the hood this would just show/hide the element instead of not rendering.

    Unfortunately, this isn't fully working yet, but here's how it can be used.

    after_mount do
      @initial_data_loaded = false
      Element['.content-loading'].hide
      Element['.loading-div'].show
    end
    
    def toggle_loading
      return if @initial_data_loaded
      if ReactiveRecord::WhileLoading.quiet?
        Element['.content-loading'].show
        Element['.loading-div'].hide
        @initial_data_loaded = true
      end
    end
    
    def render
      div do
        div.content_loading do
          ul do
            state.posts.each do |post|
              li { post.title }
              ul do
                post.comments.each do |comment|
                  li { comment.body }
                end
              end
            end
          end
        end
        div.loading_div(style: { display: 'none' }) do
          # Your loading screen
        end
      end.tap { toggle_loading }
    end
    

    It's a little dirty, but that should accomplish what you need for now, until we get WhileLoading working well. Basically the content needs to be rendered for ReactiveRecord to know to fetch and apply that data, so we need to do a show/hide instead of preventing it from rendering until the data is there.