Home / Function/ useOnRowsChange() — supabase Function Reference

useOnRowsChange() — supabase Function Reference

Architecture documentation for the useOnRowsChange() function in Grid.utils.tsx from the supabase codebase.

Entity Profile

Dependency Diagram

graph TD
  13c29bbf_8511_5da2_f728_9f9db5e200c0["useOnRowsChange()"]
  195c5181_2e45_3007_560a_5b290d5d2777["Grid()"]
  195c5181_2e45_3007_560a_5b290d5d2777 -->|calls| 13c29bbf_8511_5da2_f728_9f9db5e200c0
  style 13c29bbf_8511_5da2_f728_9f9db5e200c0 fill:#6366f1,stroke:#818cf8,color:#fff

Relationship Graph

Source Code

apps/studio/components/grid/components/grid/Grid.utils.tsx lines 22–172

export function useOnRowsChange(rows: SupaRow[]) {
  const isQueueOperationsEnabled = useIsQueueOperationsEnabled()
  const queryClient = useQueryClient()
  const { data: project } = useSelectedProjectQuery()
  const snap = useTableEditorTableStateSnapshot()
  const tableEditorSnap = useTableEditorStateSnapshot()
  const getImpersonatedRoleState = useGetImpersonatedRoleState()

  const { mutate: mutateUpdateTableRow } = useTableRowUpdateMutation({
    async onMutate({ projectRef, table, configuration, payload }) {
      const primaryKeyColumns = new Set(Object.keys(configuration.identifiers))

      const queryKey = tableRowKeys.tableRows(projectRef, { table: { id: table.id } })

      await queryClient.cancelQueries({ queryKey })

      const previousRowsQueries = queryClient.getQueriesData<TableRowsData>({ queryKey })

      queryClient.setQueriesData<TableRowsData>({ queryKey }, (old) => {
        return {
          rows:
            old?.rows.map((row) => {
              // match primary keys
              if (
                Object.entries(row)
                  .filter(([key]) => primaryKeyColumns.has(key))
                  .every(([key, value]) => value === configuration.identifiers[key])
              ) {
                return { ...row, ...payload }
              }

              return row
            }) ?? [],
        }
      })

      return { previousRowsQueries }
    },
    onError(error, _variables, context) {
      const { previousRowsQueries } = context as {
        previousRowsQueries: [
          QueryKey,
          (
            | {
                result: any[]
              }
            | undefined
          ),
        ][]
      }

      previousRowsQueries.forEach(([queryKey, previousRows]) => {
        if (previousRows) {
          queryClient.setQueriesData({ queryKey }, previousRows)
        }
        queryClient.invalidateQueries({ queryKey })
      })

      toast.error(error?.message ?? error)
    },
  })

  return useCallback(
    (_rows: SupaRow[], data: RowsChangeData<SupaRow, unknown>) => {
      if (!project) return

      const rowData = _rows[data.indexes[0]]
      const previousRow = rows.find((x) => x.idx == rowData.idx)
      const changedColumn = Object.keys(rowData).find(
        (name) => rowData[name] !== previousRow![name]
      )

      if (!previousRow || !changedColumn) return

      const enumArrayColumns = snap.originalTable.columns
        ?.filter((column) => {
          return (column?.enums ?? []).length > 0 && column.data_type.toLowerCase() === 'array'
        })
        .map((column) => column.name)

      const identifiers = {} as Dictionary<any>

      isTableLike(snap.originalTable) &&
        snap.originalTable.primary_keys.forEach((column) => {
          const col = snap.originalTable.columns.find((c) => c.name === column.name)
          identifiers[column.name] =
            col?.format === 'bytea'
              ? convertByteaToHex(previousRow[column.name])
              : previousRow[column.name]
        })

      if (Object.keys(identifiers).length === 0) {
        return toast('Unable to update row as table has no primary keys', {
          description: (
            <div>
              <p className="text-sm text-foreground-light">
                Add a primary key column to your table first to serve as a unique identifier for
                each row before updating or deleting the row.
              </p>
              <div className="mt-3">
                <DocsButton href={`${DOCS_URL}/guides/database/tables#primary-keys`} />
              </div>
            </div>
          ),
        })
      }

      const configuration = { identifiers }

      if (isQueueOperationsEnabled) {
        queueCellEditWithOptimisticUpdate({
          queryClient,
          queueOperation: tableEditorSnap.queueOperation,
          projectRef: project.ref,
          tableId: snap.table.id,
          table: snap.originalTable,
          row: previousRow,
          rowIdentifiers: identifiers,
          columnName: changedColumn,
          oldValue: previousRow[changedColumn],
          newValue: rowData[changedColumn],
          enumArrayColumns,
        })
      } else {
        // Default behavior: immediately save the change
        const updatedData = { [changedColumn]: rowData[changedColumn] }

        mutateUpdateTableRow({
          projectRef: project.ref,
          connectionString: project.connectionString,
          table: snap.originalTable,
          configuration,
          payload: updatedData,
          enumArrayColumns,
          roleImpersonationState: getImpersonatedRoleState(),
        })
      }
    },
    [
      getImpersonatedRoleState,
      isQueueOperationsEnabled,
      mutateUpdateTableRow,
      project,
      rows,
      snap.originalTable,
      snap.table.id,
      tableEditorSnap,
      queryClient,
    ]
  )
}

Subdomains

Called By

Frequently Asked Questions

What does useOnRowsChange() do?
useOnRowsChange() is a function in the supabase codebase.
What calls useOnRowsChange()?
useOnRowsChange() is called by 1 function(s): Grid.

Analyze Your Own Codebase

Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.

Try Supermodel Free