Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Weird flickering combining ScrollView, List, and Grid #95

Open
dgocoder opened this issue Apr 2, 2024 · 9 comments
Open

Weird flickering combining ScrollView, List, and Grid #95

dgocoder opened this issue Apr 2, 2024 · 9 comments
Labels
bug Something isn't working

Comments

@dgocoder
Copy link

dgocoder commented Apr 2, 2024

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
@dgocoder dgocoder added the bug Something isn't working label Apr 2, 2024
@pierpo
Copy link
Member

pierpo commented Apr 2, 2024

Hey!

Thank you for the issue illustrated with a video 😄
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 😱
Maybe the horizontal virtualized list will still work as a header in the Grid, I don't know either.

@dgocoder
Copy link
Author

dgocoder commented Apr 4, 2024

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.

Scrollview.mov

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?

List.mov

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>
  );
};

@pierpo
Copy link
Member

pierpo commented Apr 4, 2024

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 😊

@dgocoder
Copy link
Author

dgocoder commented Apr 6, 2024

@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>
  );
};
loadnew.mov

@pierpo
Copy link
Member

pierpo commented Apr 9, 2024

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.

@dgocoder
Copy link
Author

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.

@pierpo
Copy link
Member

pierpo commented Apr 12, 2024

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!

@dgocoder
Copy link
Author

@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?

@pierpo
Copy link
Member

pierpo commented Apr 30, 2024

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>
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants