Next.js Discord

Discord Forum

Multiple Re-renders

Answered
Donald Louch posted this in #help-forum
Open in Discord
Hey I'm not sure if this is the right server to post on! But I'm currently using NextJS 15, Supabase, Mantine, Hugeicons, and other packages to run my website!

I keeping have re render issues on a couple of pages and I can't figure out why? It renders once all good; then goes in a rendering loop.

Here's my code (the next few messages)!
Answered by Donald Louch
So the issue did seem like it was from a few incorrect calls within the CreateNewInvoice component 🤦🏻‍♂️ primarily calling an initial value with null instead of an empty string/"" and just some improper required fields not being called properly!
View full answer

6 Replies

// page.tsx

import { isUserSignedIn, userList, userRole, userData } from "@/app/actions/clerk"
import Invoices from "./Invoices"
import supabase from "@/lib/supabase"

export default async function InvoicesPage() {
    const isUser = await isUserSignedIn()
    const role = await userRole()
    const isAdmin = isUser && role === "admin" ? true : false
    const isMod = isUser && role === "moderator" ? true : false
    const isStaff = isAdmin || isMod

    const { userId } = await userData()

    const { data: invoicesData } = isAdmin 
        ? await supabase.from('Invoices').select().order('createdOn', { ascending: false }) as any 
        : await supabase.from('Invoices').select().match({ client: userId }).order('createdOn', { ascending: false }) as any

    const invoicesArray = new Map()
    invoicesData.forEach((invoice: any) => {
        const invoiceID = invoice.id.split("_")[2]

        const count = invoicesData.filter((inv: any) => inv.id.includes(invoiceID)).length - 1

        if (!invoicesArray.has(invoiceID) || new Date(invoice.createdOn) > new Date(invoicesArray.get(invoiceID).createdOn)) {
            invoicesArray.set(invoiceID, { ...invoice, multiple: count, invoiceID })
        }
    })

    const invoices = Array.from(invoicesArray.values())

    const usersList = await userList().then((res) => JSON.parse(JSON.stringify(res)))

    return <Invoices isStaff={isStaff} invoices={invoices} usersList={usersList.data} />
}
// ./Invoices

'use client'

import { BreadCrumb } from "@/app/(Components)/BreadCrumbsComponentPortal";
import CreateNewInvoice from "./CreateNewInvoice";
import InvoiceRow from "./InvoiceRow";
import { Alert, Table, Tabs, Title, Text } from "@mantine/core";
import HugeIcon from "@/app/(Components)/HugeIcon";

export default function Invoices({invoices, usersList, isStaff} : {invoices: any, usersList?: any, isStaff: boolean}){
  const breadCrumbs = [
    {"pageLink": null, "pageName": "Invoices"},
  ]

  return <>
    <BreadCrumb breads={breadCrumbs} />
    {isStaff && <CreateNewInvoice isStaff={isStaff} invoices={invoices} usersList={usersList} />}

    <Title mt="1.5rem" ta="center">Invoices</Title>
    {invoices.length === 0 ? <Alert variant="light" color="green" icon={<HugeIcon name="information-circle" variant="twotone"/>} mt="3rem"><Text my="0.5rem" c="white">There currently is no invoices that have been requested!</Text></Alert> : <Table stickyHeader stickyHeaderOffset={60} highlightOnHover borderColor="var(--darkPurple)" highlightOnHoverColor="var(--darkPurpleRGBA)" striped="even" stripedColor="var(--blackRGBA)" p="1rem" style={{boxShadow: "var(--mantine-shadow-bsSMSecondary)", borderRadius: "var(--mantine-radius-md)", overflow: "hidden"}} my="2rem">
      <Table.Tbody>
        {invoices.map((invoice: any) => (
          <InvoiceRow key={invoice.id} invoice={invoice} isStaff={isStaff} />
        ))}
      </Table.Tbody>
    </Table>
    }
  </>
}
Part 1
// ./InvoiceRow

'use client'

...

export default function InvoiceRow({ invoice, isStaff }: { invoice: any, isStaff: boolean }) {   
const invoiceID = invoice.invoiceID;

const router = useRouter()

function openProject() {
  router.push(`invoice/${invoiceID}`)
}

const currentTime = new Date()
return <>
  <Table.Tr key={invoice.id}>
    <Table.Td w="40%">
      <Stack gap="1rem" m='1rem'>
        <Group>
          <Badge color="primary" leftSection={<HugeIcon name="grid" variant="twotone" />}>
            {invoiceID}
          </Badge>
          {isStaff && <Badge color="secondary" leftSection={<HugeIcon name="id" />}>
            {invoice.client}
          </Badge>}
        </Group>

        <Group>
          <Group>
            <InvoiceTypeBadge type={invoice.invoiceType} />
            {invoice.versionNumber && 
              <Badge color="gray">
                Version {invoice.versionNumber}
              </Badge>
            }
          </Group>
          {invoice.multiple > 0 &&
            <Badge color="blue">
              {invoice.multiple} Other versions available
            </Badge>
          }
        </Group>
      </Stack>
    </Table.Td>

    <Table.Td w="10%" ta="center">
      <Stack gap="0">
        <Tooltip label={invoice.createdOn ? moment(invoice.createdOn!).format("MMMM Do, YYYY [at] h:mma") : "Not Listed"}>
          <Text fz="sm" fw="600">{invoice.createdOn ? moment(invoice.createdOn!).format("MM-DD-YYYY") : "Not Listed"}</Text>
        </Tooltip>
        <Text fz="xs" c="dimmed" mt="-1rem">
          Created On
        </Text>
      </Stack>
    </Table.Td>
Part 2
// ./InvoiceRow

  <Table.Td w="10%" ta="center">
      <Stack gap="0">
        <Tooltip label={invoice.paidOn ? <DisplayDate source={invoice.paidOn} /> : invoice.dueOn ?  <DisplayDate source={invoice.dueOn} /> : "Not Listed"}>
          <Text fz="sm" fw="600" c={ !invoice.dueOn ? "white" : currentTime < new Date(invoice.dueOn) ? "green" : invoice.paidOn && new Date(invoice.paidOn) < new Date(invoice.dueOn) ? "green" :  "red" }>{invoice.paidOn ? <DisplayDate source={invoice.paidOn} format="MM-DD-YYYY" />  : invoice.dueOn ? <DisplayDate source={invoice.dueOn} format="MM-DD-YYYY" /> : "Not Listed"}</Text>
        </Tooltip>
        <Text fz="xs" c="dimmed" mt="-1rem">
          {invoice.paidOn ? "Paid On" : "Due On"}
        </Text>
      </Stack>
    </Table.Td>

    <Table.Td w="10%" ta="center">
      <Stack gap="0">
        <Text fz="sm" fw="600" c={invoice.owing > 0 ? "red" : "green"}>
          <CurrencyFormat amount={invoice.owing} />
        </Text>
        <Text fz="xs" c="dimmed" mt="-1rem">
          Left Owing
        </Text>
      </Stack>
    </Table.Td>

    <Table.Td w="15%" ta="right">
      <Tooltip label={`This invoice is primarily related to a ${invoice.relatedItem?.type.toLowerCase()}`}>
        <Badge color="gray" leftSection={invoice.relatedItem?.type === "Project" ? <HugeIcon name="files-01" /> : invoice.relatedItem?.type === "Task" ? <HugeIcon name="task-01" /> : <HugeIcon name="grid" />}>
          {invoice.relatedItem?.id}
        </Badge>
      </Tooltip>
    </Table.Td>

    <Table.Td w="12.5%" ta="left">
      <InvoiceStatusBadge status={invoice.status} />
    </Table.Td> 

    <Table.Td w="2.5%" ta="center">
      <Tooltip label="View Invoice">
        <Box><HugeIcon name="view" variant="duotone" clickOption={openProject} /></Box>
      </Tooltip>
    </Table.Td>
  </Table.Tr>
</> 
}
@Donald Louch ts // ./Invoices 'use client' import { BreadCrumb } from "@/app/(Components)/BreadCrumbsComponentPortal"; import CreateNewInvoice from "./CreateNewInvoice"; import InvoiceRow from "./InvoiceRow"; import { Alert, Table, Tabs, Title, Text } from "@mantine/core"; import HugeIcon from "@/app/(Components)/HugeIcon"; export default function Invoices({invoices, usersList, isStaff} : {invoices: any, usersList?: any, isStaff: boolean}){ const breadCrumbs = [ {"pageLink": null, "pageName": "Invoices"}, ] return <> <BreadCrumb breads={breadCrumbs} /> {isStaff && <CreateNewInvoice isStaff={isStaff} invoices={invoices} usersList={usersList} />} <Title mt="1.5rem" ta="center">Invoices</Title> {invoices.length === 0 ? <Alert variant="light" color="green" icon={<HugeIcon name="information-circle" variant="twotone"/>} mt="3rem"><Text my="0.5rem" c="white">There currently is no invoices that have been requested!</Text></Alert> : <Table stickyHeader stickyHeaderOffset={60} highlightOnHover borderColor="var(--darkPurple)" highlightOnHoverColor="var(--darkPurpleRGBA)" striped="even" stripedColor="var(--blackRGBA)" p="1rem" style={{boxShadow: "var(--mantine-shadow-bsSMSecondary)", borderRadius: "var(--mantine-radius-md)", overflow: "hidden"}} my="2rem"> <Table.Tbody> {invoices.map((invoice: any) => ( <InvoiceRow key={invoice.id} invoice={invoice} isStaff={isStaff} /> ))} </Table.Tbody> </Table> } </> }
I have narrowed the issue down (I think)?! I do believe it's my {isStaff && <CreateNewInvoice isStaff={isStaff} invoices={invoices} usersList={usersList} />} component?!
So the issue did seem like it was from a few incorrect calls within the CreateNewInvoice component 🤦🏻‍♂️ primarily calling an initial value with null instead of an empty string/"" and just some improper required fields not being called properly!
Answer