Synchronizing state between different components in Litho
This is the fifth post in the series - Exploring Litho. In the first post - Android GIF search engine with Litho, we explored some key aspects of Litho - LayoutSpec, MountSpec, Use Glide to load images, Update RecyclerView data, etc. In the second post - Managing State in Litho, we dabbled around with state in Litho components. In the third post - Navigation with Litho, we used LithoView
and components to navigate instead of an Acitivity or a Fragment. If you’re not familiar with Litho, I would suggest you to go through the previous posts. We explored Events and how to use them to propagate changes in the fourth post - Using Events in Litho.
We are going to explore some ways using which we can synchronize and update component state. When we like a GIF in full screen and come back, we need to update the state of that component in RecyclerView. As we have seen in the previous posts, that updating a component’s state from outside is not possible. So we somehow need to use the component to update its state. We are going to achieve this with the help of Events.
Let’s add an event FavChangeEvent
which we’ll use to update the state of GifItemView
component. We’ll call this event from outside of GitItemView component. We already have LikeChangeEvent
which we use internally to update the state of liked
and invoke callback to update our local database.
We want to listen to FavChangeEvent
and update state. This event should not invoke callback to update database. Let’s add this event to GitItemViewSpec
.
You can find complete code here - GifItemViewSpec.
Now, we can use onFavChanged to update isLiked
State. Let’s modify FullScreenComponentSpec
so that it can send FavChangeEvent
when like button is clicked.
You can find complete code here - FullScreenComponentSpec.
When we receive ClickEvent
, onLikeButtonClicked method will be invoked. We need to do 3 things here.
- Update
isLiked
state of FullScreenComponent. - Update database.
- Send an event so that
isLiked
state of GifItemView component is also updated.
FullScreenComponent.updateLikeButtonAsync(c, !isLiked)
will update its ownisLiked
state.FullScreenComponent.dispatchLikeChangeEvent(FullScreenComponent.getLikeChangeEventHandler(c), !isLiked, gif.getId())
will update the database. We can use a simple callback here instead of Event. And it’s recommended to use a callback because working with Event generates a lot og boilerplate code.EventHandler<FavChangeEvent> handler = GifItemView.onFavChanged(c)
will create FavChangeEvent which we will dispatch to update GifItemView state.
So let’s try it out.
Did not work!
Debugging
At the time, I had no clue why it did not work. So I started debugging and added bunch of breakpoints all over the place especially in GifItemView#dispatchOnEvent()
. That method has switch case based on event id and calls appropriate methods. I started debugging and realized that after dispatching event from FullScreenComponent, GifItemView is never receiving the event to dispatch. That seems odd. What could be the reason for this?
- How does FullScreenComponent know which GifItemView component to send the event to?
- Does it work like EventBus? Litho component listens to the events and there’s a central dispatcher which sort of broadcasts events? That seems unlikely.
I needed to debug more to understand how these events are sent. I added breakpoints to FullScreenComponent generated class and started debugging again. I realized that FullScreenComponent#dispatchOnEvent
was being called when we were dispatching FavChangeEvent. After digging further, I realized that the EventHandler
was created using ComponentContext
of FullScreenComponent and hence the EventDispatcher
used by that EventHandler also belonged to FullScreenComponet’s ComponentContext and it would send the event to that component itself!
Wow! Such intricate architecture! I’m so happy.
ComponentContext
Litho is so context aware! In this series, we have found some things which just don’t work if you don’t have the correct context. Let’s add this to the list. Being so context aware is really a great thing and I can only wonder and admire the thoughts that went into building such complex yet so easy to use framework.
Each component has its own context and even context of the same component class are useless. We need the context of that component!
So let’s get back to fixing the issue.
ContextAwareEventHandler
We need GifItemView
component in FullScreenComponet for state synchronization to work. So let’s pass it as a @Prop
.
You can find complete code here - FullScreenComponentSpec.
For this, we also need to update some other code and we need to pass GifItemView component in callback.
You can find complete code here - GifItemViewSpec.
Now, that we have updated the callback and we are passing GifItemView
as @Prop, we also need to update some other files.
You can find complete code here - MainActivity
Let’s try it out! Yes, and it does work!
We have also fixed the issue where if user opnes a big view from a smaller view and updates like, etc, we can easily propagate those changes back to the smaller component. Small things as these are really a great UX win!
Code
You can find current code here - LithoGifDemo - v6-state-sync
You can find the latest code (keeps updating) here - LithoGifDemo
In the next post, we will explore how we can use MVP architecture with Litho to build an application that can scale. Litho components are really declerative and can be an ideal View in MVP.
P.S. My experience with Litho has been amazing. It has great potential and if we can make a scalable MVP using Litho, it would be a great step in validating Litho. Of course, Facebook uses it in the Android app so we really don’t need a better validation than that.
Series
- Android GIF search engine with Litho
- Managing State in Litho
- Navigation with Litho
- Events with Litho
- Synchronizing state between different components
Litho - Facebook's declarative UI framework
Explore Facebook's brilliant UI framework through the journey of making an android app to search GIFs. Build your own app with Litho as we analyze Litho and learn about its Components, State, Navigation, Events and synchronization.