nextui-org / nextui

🚀 Beautiful, fast and modern React UI library.
https://nextui.org
MIT License
22.18k stars 1.56k forks source link

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

Open jimmyzzxhlh opened 6 months ago

jimmyzzxhlh commented 6 months ago

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:

  const [rowsPerPage, setRowsPerPage] = useState<number>(10);
  const [page, setPage] = useState<number>(1);

  const pages = useMemo(
    () => Math.ceil(items.length / rowsPerPage),
    [items.length, rowsPerPage]
  );
      <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

linear[bot] commented 6 months ago

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

bietiaop commented 5 months ago

I'm having the same problem.

alphaxek commented 5 months ago

Hi @jimmyzzxhlh ,

See if having initialPage helps image

bietiaop commented 5 months ago

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 commented 5 months ago

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.

alphaxek commented 5 months ago

@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

jorgerojas26 commented 5 months ago

Exact same issue here

alphaxek commented 5 months ago

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

bietiaop commented 5 months ago

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

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

jimmyzzxhlh commented 5 months ago

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

TomTom-Labs commented 5 months ago

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.

arindam1997007 commented 5 months ago

hi @wingkwong . Can I work on this bug?

wingkwong commented 5 months ago

@arindam1997007 go ahead.

arindam1997007 commented 5 months ago

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>
bietiaop commented 5 months ago

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.

d3rd4nnyLABS commented 4 months ago

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

wingkwong commented 4 months ago

@d3rd4nnyLABS I'll take a look tonight

arindam1997007 commented 4 months ago

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

HickAndrade commented 4 months ago

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.

stefnto commented 2 months ago

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

bietiaop commented 1 month ago

@wingkwong Is this issue not fixed yet?

wingkwong commented 1 month ago

@bietiaop no. the pr requires changes.

bietiaop commented 1 month ago

@bietiaop no. the pr requires changes.

ok

Marcin-Palubinski commented 1 month ago
    <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.

AntoineArt commented 1 month ago
    <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