nextui icon indicating copy to clipboard operation
nextui copied to clipboard

[BUG] Pagination left arrow shows page 1 on the first load

Open jimmyzzxhlh opened this issue 1 year ago • 23 comments

NextUI Version

2.4.1

Describe the bug

Pagination component is showing the left arrow button as page number 1. This only happens for the first time when the page is loaded. If I click a different page, the left arrow will be displayed correctly.

image

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

I'm not able to reproduce this in Stackblitz using static data. What I'm doing is:

  • Load data from a Graphql endpoint.
  • Calculate the number of pages needed based on the number of items loaded, and a rowsPerPage parameter:
  const [rowsPerPage, setRowsPerPage] = useState<number>(10);
  const [page, setPage] = useState<number>(1);

  const pages = useMemo(
    () => Math.ceil(items.length / rowsPerPage),
    [items.length, rowsPerPage]
  );
  • Pass the page number to the Pagination component.
      <div className="py-2 px-2 flex items-center justify-center">
        <Pagination
          isCompact
          showControls
          showShadow
          color="primary"
          page={page}
          total={pages}
          onChange={(page) => setPage(page)}
        />
      </div>

Expected behavior

Left arrow should be correctly displayed.

Screenshots or Videos

No response

Operating System Version

macOS

Browser

Chrome

jimmyzzxhlh avatar May 31 '24 23:05 jimmyzzxhlh

I'm having the same problem.

bietiaop avatar Jun 08 '24 09:06 bietiaop

Hi @jimmyzzxhlh ,

See if having initialPage helps image

alphaxek avatar Jun 08 '24 14:06 alphaxek

Hi @jimmyzzxhlh ,

See if having initialPage helps image

I've tried. It didn't work.

  // other codes
  const [page, setPage] = React.useState(1);
  const pages = React.useMemo(() => {
    const pages = Math.ceil(sortedItems.length / rowsPerPage) || 1;
    return pages;
  }, [sortedItems.length, rowsPerPage]);
  const bottomContent = React.useMemo(() => {
    return (
      <div className="flex justify-end">
        <Pagination
          showControls
          showShadow
          color="primary"
          initialPage={1}
          page={page}
          total={pages}
          onChange={setPage}
        />
      </div>
    );
  }, [page, pages]);
  // other codes

bietiaop avatar Jun 08 '24 15:06 bietiaop

Hi @jimmyzzxhlh ,

See if having initialPage helps image

My page implementation looks like this: First there is a list storing the ids:

const [list, setList] = React.useState<number[]>([]);

Then the custom component is rendered through the list (similar to the code below, possibly with some extra code for processing):

// ... other code
const renderItem = (id: number) => {
  return <CustomComponent id={id} />
}

// ... other code
return (
  <div className="...">
    list.map(id=>{
      <div className="...">{renderItem(id)}</div>
    })
   </div>
)

In CustomComponent there are some Tabs:

const CustomComponent: React.FC<{id: number}> = React.memo(({id}) => {
  const [active, setActive] = React.useState('edit');
  const tabs = [
    { key: 'edit', title: 'Edit', component: <EditPage id={id} /> },
    { key: 'team', title: 'Team', component: <TeamPage id={id} /> },
  ];
  if(someConditions) {
    tabs.push({ key: 'review', title: 'Review', component: <ReviewPage id={id} /> })
  }
  return (
    <div className="...">
      <div className="...">
        <Tabs
          aria-label="Options"
          selectedKey={active}
          onSelectionChange={(key) => {
            setActive(key as string);
          }}
        >
          {tabs?.map((tab) => <Tab key={tab.key} title={tab.title} />)}
        </Tabs>
      </div>
      <div className="...">
        {tabs?.map((tab) => (
          <div key={tab.key} className={active === tab.key ? '' : 'hidden'}>
            {tab.component}
          </div>
        ))}
      </div>
    </div>
  );
})

In EditPage, TeamPage, etc., the data is fetched and presented in a table, which refers to the Use Case Example on the NextUI docs. The content includes the pagination component code mentioned in the last comment, the active cursor “1” jumps to the “previous page” position.

bietiaop avatar Jun 08 '24 15:06 bietiaop

@jimmyzzxhlh I tried reproducing the issue but I couldn't reproduce, do you have any sandbox link with minimal code to play around with and see what the issue is

alphaxek avatar Jun 08 '24 19:06 alphaxek

Exact same issue here

jorgerojas26 avatar Jun 11 '24 02:06 jorgerojas26

Hi @jorgerojas26 can you please share the minimal code to reproduce the issue

alphaxek avatar Jun 11 '24 10:06 alphaxek

Hi @jorgerojas26 can you please share the minimal code to reproduce the issue

Sandbox link: https://codesandbox.io/p/devbox/vsw923?migrateFrom=q2h7q8

bietiaop avatar Jun 16 '24 19:06 bietiaop

@bietiaop Thank you, this is exactly what I'm seeing.

jimmyzzxhlh avatar Jun 17 '24 22:06 jimmyzzxhlh

I see that same (wrong) behavior. It happens to me, when the Pagination is in an modal dialog (HTML

).

One thing we realized is that the translateX of the showing the indicator is set to 0 - should be 40. This only happens in the dialog - on a regular page it works with the same source code.

TomTom-Labs avatar Jun 18 '24 14:06 TomTom-Labs

hi @wingkwong . Can I work on this bug?

arindam1997007 avatar Jun 25 '24 16:06 arindam1997007

@arindam1997007 go ahead.

wingkwong avatar Jun 25 '24 17:06 wingkwong

Hi @jimmyzzxhlh

Could you please confirm if you are hiding the pagination component using display:none until the data is loaded from the GraphQL endpoint?

Hi @bietiaop

For the timebeing, can you verify once, if instead of hiding the inactive tab using display:none, you don't re-render it. Perhaps something like this?

const activeTab = tabs?.find((tab) => tab.key === active);

return <div>
...
 {activeTab && <div key={activeTab.key}>{activeTab.component}</div>}
</div>

arindam1997007 avatar Jun 26 '24 15:06 arindam1997007

Hi @jimmyzzxhlh

Could you please confirm if you are hiding the pagination component using display:none until the data is loaded from the GraphQL endpoint?

Hi @bietiaop

For the timebeing, can you verify once, if instead of hiding the inactive tab using display:none, you don't re-render it. Perhaps something like this?

const activeTab = tabs?.find((tab) => tab.key === active);

return <div>
...
 {activeTab && <div key={activeTab.key}>{activeTab.component}</div>}
</div>

That's what I was trying to use display:none to avoid re-rendering. Re-rendering of these components can lead to pointless duplicate requests.

bietiaop avatar Jun 26 '24 15:06 bietiaop

@wingkwong @arindam1997007 Are you still working on the open pull request? The pull request seems to only fail the "Authorization required to deploy" check. The Bug is currently blocking us a bit. If this can be resolved, it would be awesome!

d3rd4nnyLABS avatar Jul 11 '24 08:07 d3rd4nnyLABS

@d3rd4nnyLABS I'll take a look tonight

wingkwong avatar Jul 11 '24 08:07 wingkwong

@wingkwong @arindam1997007 Are you still working on the open pull request? The pull request seems to only fail the "Authorization required to deploy" check. The Bug is currently blocking us a bit. If this can be resolved, it would be awesome!

Hey. It's under review as of now. I will fix any reviews if it comes and then @wingkwong will help with the merging.

arindam1997007 avatar Jul 11 '24 09:07 arindam1997007

Hello everyone,

I initially encountered the same problem. After updating the UI library, the component started to render like this:

image

My temporary solution was to trigger a page switch, because it always returned to normal when I selected another page and then went back to the first one. (I like this library, even though it isn't receiving the attention it deserves).

Here is a simple example using useEffect:

const rowsPerPage: number = 5;

  const pages = Math.ceil(listar.length / rowsPerPage);

  const items = useMemo(() => {
    const start = (page - 1) * rowsPerPage;
    const end = start + rowsPerPage;
    return listar.slice(start, end);
  }, [page, listar]);
  
   useEffect(() => {
    if (pages > 1) {
      setPage(2);
      setTimeout(() => setPage(1), 0);
    }
  }, [pages]);

I hope this helps.

HickAndrade avatar Jul 18 '24 13:07 HickAndrade

Having the same issue,when showControls prop is passed, true or false. Removing the prop solves the issue, so probably the problem lies there.

stefnto avatar Sep 07 '24 16:09 stefnto

@wingkwong Is this issue not fixed yet?

bietiaop avatar Oct 06 '24 12:10 bietiaop

@bietiaop no. the pr requires changes.

wingkwong avatar Oct 06 '24 12:10 wingkwong

@bietiaop no. the pr requires changes.

ok

bietiaop avatar Oct 06 '24 13:10 bietiaop

    <Pagination
      loop
      showControls
      total={total}

      page={page > total ? total : page}

      onChange={setPage}
    />

My total is also based on items.length. Adding condition and fallback to page parameter fixed all isues with first render.

Marcin-Palubinski avatar Oct 23 '24 19:10 Marcin-Palubinski

    <Pagination
      loop
      showControls
      total={total}

      page={page > total ? total : page}

      onChange={setPage}
    />

My total is also based on items.length. Adding condition and fallback to page parameter fixed all isues with first render.

Thx for the workaround, it works for me

AntoineArt avatar Oct 24 '24 15:10 AntoineArt