react-tv-space-navigation
react-tv-space-navigation copied to clipboard
Weird flickering combining ScrollView, List, and Grid
Describe the bug For whatever reason navigating down on a grid view inside of a scrollview is causing some weird flickering instead of working as expected. If I do not have it in scroll view it works fine. If i load the page and defer the grid from showing it works fine. However using a key on the grid causes this issue.
To Reproduce
const [catFilter, setCatFilter] = useState("Todas");
const setOndemandFilter = useCallback((cat: Category) => {
setCatFilter(cat);
}, []);
retrun (<Page><DefaultFocus>
<SpatialNavigationScrollView
offsetFromStart={140}
style={{ backgroundColor: "black" }}
>
<View className="flex-1 bg-black px-4 pt-[18.5px]">
<View className="h-32">
<Text
style={{ fontSize: 20 }}
className="pb-2 font-medium text-white"
>
Categorias
</Text>
<SpatialNavigationVirtualizedList
orientation="horizontal"
data={categoriesIndexed}
renderItem={renderCategory}
itemSize={178}
numberOfRenderedItems={WINDOW_SIZE}
numberOfItemsVisibleOnScreen={NUMBER_OF_ITEMS_VISIBLE_ON_SCREEN}
onEndReachedThresholdItemsNumber={
NUMBER_OF_ITEMS_VISIBLE_ON_SCREEN
}
/>
</View>
<Text
style={{ fontSize: 20 }}
className="py-2 font-medium text-white"
>
{categoryMapping[catFilter] ?? "Todas"}
</Text>
<SpatialNavigationVirtualizedGrid
data={schedulePrograms}
key={catFilter}
style={{ backgroundColor: "#000000" }}
renderItem={renderItem}
itemHeight={207 * 1.075}
numberOfColumns={NUMBER_OF_COLUMNS}
numberOfRenderedRows={NUMBER_OF_RENDERED_ROWS}
numberOfRowsVisibleOnScreen={NUMBER_OF_ROWS_VISIBLE_ON_SCREEN}
onEndReachedThresholdRowsNumber={INFINITE_SCROLL_ROW_THRESHOLD}
scrollInterval={150}
/>
</View>
</SpatialNavigationScrollView>
</DefaultFocus></Page>)
Expected behavior Would expect the grid to work as normal
Screenshots https://github.com/bamlab/react-tv-space-navigation/assets/13984190/580e26a2-2b6f-4884-83c0-1fda10d88103
Version and OS
- Library version: 3/1.2
- React Native version: 0.73.6
- OS [e.g. Android, web]: Android
Hey!
Thank you for the issue illustrated with a video :smile: Looks like the ScrollView makes the grid all buggy indeed.
However, I am noticing that you might want to use the header props of the grid component. Have you tried it? :) You should be able to achieve the same layout, and I'm pretty sure it will work properly in that case!
Edit: oh, I hadn't noticed that you had a horizontal scrollable content for your categories. Maybe a scroll view inside the header might work. I haven't tried though :scream:
Maybe the horizontal virtualized list will still work as a header in the Grid, I don't know either.
Hey @pierpo thanks for quick response. So the scrollview worked however the focus coming back is jumping back to the same number in the grid.
https://github.com/bamlab/react-tv-space-navigation/assets/13984190/ebc1011a-b451-4a49-a7df-afba5c6a5859
Using the list sort of does the inverse as you can see. Also with both scrollview and list in the on select in the header for whatever reason its jumping down in the screen. Any thoughts?
https://github.com/bamlab/react-tv-space-navigation/assets/13984190/bc359bf8-620e-4624-97b0-47df87be8d36
code below is just mainly the move to the category section/todo title into header component as you suggested.
const Header = useCallback(
() => (
<View style={{ width: 1920 }}>
<Text style={{ fontSize: 20 }} className="pb-2 font-medium text-white">
Categorias
</Text>
<SpatialNavigationVirtualizedList
orientation="horizontal"
data={categoriesIndexed}
renderItem={renderCategory}
itemSize={178}
numberOfRenderedItems={WINDOW_SIZE}
numberOfItemsVisibleOnScreen={6}
onEndReachedThresholdItemsNumber={6}
/>
<Text
style={{
fontSize: 20,
padding: 0,
marginTop: -5,
}}
className="font-medium text-white"
>
{categoryMapping[catFilter] ?? "Todas"}
</Text>
</View>
),
[catFilter],
);
return (
<Screen>
{/* <DefaultFocus> */}
<SpatialNavigationScrollView
offsetFromStart={140}
style={{ backgroundColor: "black" }}
>
<View className="h-[1000px] bg-black px-4 pt-[18.5px]">
{/* {!moviesLoading ? ( */}
<SpatialNavigationVirtualizedGrid
data={schedulePrograms}
style={{ backgroundColor: "#000000" }}
renderItem={renderItem}
itemHeight={207 * 1.075}
header={<Header />}
headerSize={180}
numberOfColumns={NUMBER_OF_COLUMNS}
numberOfRenderedRows={NUMBER_OF_RENDERED_ROWS}
numberOfRowsVisibleOnScreen={NUMBER_OF_ROWS_VISIBLE_ON_SCREEN}
onEndReachedThresholdRowsNumber={INFINITE_SCROLL_ROW_THRESHOLD}
scrollInterval={150}
/>
{/* ) : (
<Loader />
)} */}
</View>
</SpatialNavigationScrollView>
{/* </DefaultFocus> */}
</Screen>
);
};
Thank you for the very detailed answers, that's awesome :)
I just had an idea 😎 Could you wrap your header with a SpatialNavigationNode ? No props, nothing. It will probably absorb the grid alignment. I haven't checked, but there's a good chance it will work 😊
@pierpo unfortunately adding SpatialNavigationNode did not solve it.
const Header = useCallback(
() => (
<SpatialNavigationNode>
<View style={{ width: 1920 }}>
<Text
style={{ fontSize: 20 }}
className="pb-2 font-medium text-white"
>
Categorias
</Text>
<SpatialNavigationVirtualizedList
orientation="horizontal"
data={categoriesIndexed}
renderItem={renderCategory}
itemSize={178}
numberOfRenderedItems={WINDOW_SIZE}
numberOfItemsVisibleOnScreen={6}
onEndReachedThresholdItemsNumber={6}
/>
<Text
style={{
fontSize: 20,
padding: 0,
marginTop: -5,
}}
className="font-medium text-white"
>
{categoryMapping[catFilter] ?? "Todas"}
</Text>
</View>
</SpatialNavigationNode>
),
[catFilter],
);
return (
<Screen>
<DefaultFocus>
<SpatialNavigationScrollView
offsetFromStart={140}
style={{ backgroundColor: "black" }}
>
<View className="h-[1000px] bg-black pr-4 pt-[18.5px]">
{moviesLoading ? (
<Loader />
) : (
<SpatialNavigationVirtualizedGrid
data={schedulePrograms}
style={{ backgroundColor: "#000000" }}
renderItem={renderItem}
itemHeight={207 * 1.075}
header={<Header />}
headerSize={185}
numberOfColumns={NUMBER_OF_COLUMNS}
numberOfRenderedRows={NUMBER_OF_RENDERED_ROWS}
numberOfRowsVisibleOnScreen={NUMBER_OF_ROWS_VISIBLE_ON_SCREEN}
onEndReachedThresholdRowsNumber={INFINITE_SCROLL_ROW_THRESHOLD}
scrollInterval={150}
/>
)}
</View>
</SpatialNavigationScrollView>
</DefaultFocus>
</Screen>
);
};
https://github.com/bamlab/react-tv-space-navigation/assets/13984190/3ddbd657-3f7c-42ae-a114-9255759dc6d2
Indeed! Thank you for all the videos, @dgocoder. That really makes it simpler.
About the problem, I think I have an idea. I need to try it out. I think the header is wrapped in the same SpatialNavigationNode with grid enabled, so it behaves like a grid, as the rest. If we split the header and the content of the grid into two different nodes, it will probably work.
One problem will remain though, for which I have no idea of a simple solution: since the grid is virtualized, at some point the row above in the header will be unmounted... And it will lose track of the focused item.
Btw, have you considered implementing a non virtualized grid? If you are guaranteed to have no more than ~100 elements, I think the grid would work decently in most cases for most devices 😄 And you'd have more control on it.
Unfortunately I do have more than 100 elements. In regard to it losing focus, ideally if I didn't have to put the virtualized list in the header (which we did because of the original issue) then perhaps we wouldn't have to worry about it not rendering. I really don't need the horizontal list to be virtualized.
Unfortunately, the problem lies in the Grid and not the virtualized list above. The header is actually... a grid element. So it inherits the grid system.
We need to change that!
@pierpo while I know the header is a grid element, what if we didn't use header component? When I tried to do so we had a different issue with scrolling but in reality it shouldn't have to be the header. I know that was an attempt to solve the issue by using header but perhaps theres another remedy?
Hey @dgocoder!
The problem with not using the header props is that since the virtualized grid has a fake CSS scroll, you might not be able to have something that looks nice. Unless you try to hide the header differently by detecting whether it is active or not (using SpatialNavigationNode's isActive child property), but that might look ugly. It's something that can be tried out though, if you manage to do a translate animation similar to the grid then it might be fine 😁
<HeaderRow />
<VirtualizedGrid />
with something that would be like this (assuming that shouldBeVisible triggers an animation of your choice)
const HeaderRow = () => {
return <SpatialNavigationNode>{({isActive}) => <Header shouldBeVisible={!!isActive} />}</SpatialNavigationNode>
}