One of the key feature of a good UX is the ability of the user to access relevant information with minimum interactions. There are many UI patterns which focuses on enabling this behaviour, these are largely based on the use cases.
One such case is when we have screens containing multiple items and user has to scroll through the screen to reach to the most relevant item. One way to solve for long scrolls is by using autoscroll, so the user lands on the desired item directly. So essentially, based on the choices made by the user earlier, we can directly scroll to the most relevant item while still providing the freedom to manually scroll to other products.
Other such use case of auto scroll would be when we have a deep link to a item in the screen with scroll. A good UX would focus on redirecting to the desired screen and auto scrolling to the item which was deep linked. To solve for these scenarios in a react native application, I have implemented auto scrolling which works for both android and IOS.
Background:
Screen A: dropdown with items and a button to navigate to the screen B.
Screen B: multiple item cards with item details.
User selects one item from the dropdown on screen A and navigates to screen B, where item details are fetched and the screen is then autoscrolled to the selected item.
Auto scroll is provided out of the box by react native. The ScrollView component by react native has props to scroll to the desired co-ordinates on the screen. There are two such props in ScrollView:
- ScrollTo(): takes the x and y offset coordinates and scrolls to that position.
- ScrollToEnd(): Scrolls to the end of the list
Both of these props accepts animation option which controls the animations. This option allows to define the user experience while autoscrolling, if we want a smooth transition or jump immediately to the item.
The primary challenge was finding out the co-ordinates of the item. The accurate position of the section will only be available once all the items in the list have rendered and initial animations are over.
Implementation:
- Identify the item to scroll to: Send the unique itemId for the item which was selected from the drop down on screen A.
- Store item card ref: While rendering the item cards, check for the item with the selected itemId and stored the ref for the item.
setItemRef= (itemId) => (element) =>
{
const {selectedItemId} = this.props.navigation.state.params;
if(selectedItemId === itemId && && !this.itemRef)
this.itemRef = element;
}
3. Store scroll view ref: Store reference to the scroll view component.
setScrollViewRef = (element) => {
this.scrollViewRef = element;
};<ScrollView ref={this.setScrollViewRef}>
...
</ScrollView>
We now have the reference to both the scroll view and the item to be scrolled to. Next we need a method that would scroll using the references.
scrollToItem = () => {
this.productCardRef.measureLayout(
ReactNative.findNodeHandle(this.scrollViewRef),
(x, y) => {
this.scrollViewRef.scrollTo({x: 0, y: y, animated: true});
}
);
}
Here scrollToItem uses measureLayout which provides the x and y offset of the item relative to the scroll view node. After we have the relative position, we can use scrollTo to autoscroll to the item.
Next challenge was when to trigger this method. Since I had an API call on load of the screen to fetch item details. Auto scroll should happen when the the data is fetched and items are rendered.
React fiber allows to use requestAminationFrame to solve for these scenarios where we want to schedule a high priority task to be executed in the next animation frame. Thus, the final version of the method looks like this:
scrollToItem = () => {
requestAnimationFrame(() => {
if (this.itemCardRef && this.scrollViewRef) {
this.itemCardRef.measureLayout(
findNodeHandle(this.scrollViewRef),(x, y) => {
this.scrollViewRef.scrollTo({x: 0, y: y, animated:true});
});
}
});
}
That’s all folks! 😇