Skip to content

Instantly share code, notes, and snippets.

@a1300
Created June 1, 2025 15:15
Show Gist options
  • Save a1300/eeb5b3978b444f9819e269263a416d0c to your computer and use it in GitHub Desktop.
Save a1300/eeb5b3978b444f9819e269263a416d0c to your computer and use it in GitHub Desktop.
svg
@a1300
Copy link
Author

a1300 commented Jul 6, 2025

@a1300
Copy link
Author

a1300 commented Jul 7, 2025

@a1300
Copy link
Author

a1300 commented Jul 8, 2025

To handle dependent queries in TanStack React Query (react-query), you typically want the second query to wait until the first query has completed and provided the necessary data. The key is to use the enabled option in your second query, so it only runs when the first query is successful and its data is available.

Here’s how you can do it:

import { useQuery } from '@tanstack/react-query';

// First query
const { data: firstData, isLoading: loadingFirst } = useQuery({
  queryKey: ['first'],
  queryFn: fetchFirstData,
});

// Second query, only enabled when firstData is available
const { data: secondData, isLoading: loadingSecond } = useQuery({
  queryKey: ['second', firstData?.id], // Use firstData as part of the key if relevant
  queryFn: () => fetchSecondData(firstData.id),
  enabled: !!firstData, // Only run when firstData exists
});

Explanation:

  • The first query fetches some data.
  • The second query is dependent on the data from the first query. It uses the enabled property to ensure it only runs when firstData is available (i.e., after the first query has finished successfully).
  • This pattern ensures the second request waits for the first to complete.

References:

Let me know if you need a more detailed example!

@a1300
Copy link
Author

a1300 commented Jul 12, 2025

use ref for performance gains:

https://www.youtube.com/watch?v=TgpTG5XYoz4

@a1300
Copy link
Author

a1300 commented Jul 12, 2025

Great question! To efficiently sync a user's interests (stored as a JS array) with your Drizzle/Postgres backend when they update their interests via a mutation, you should:

1. Data Model

Assume:

  • persons table (id, name, ...)
  • interests table (id, name)
  • person_interests table (person_id, interest_id) — many-to-many relationship

2. Frontend Flow

  • User sends the full, updated array of interests (e.g. ["music", "coding", "cooking"]) when they save.

3. Backend Mutation Steps

a. Upsert interests

  • For each interest in the array, ensure it exists in the interests table.
    • Use Drizzle’s insert with onConflictDoNothing (or similar) to avoid duplicates.

b. Fetch Interest IDs

  • Retrieve all interest IDs for the submitted names.

c. Sync Person's Interests

  • Fetch the current interests for the person from person_interests.
  • Compute:
    • To Add: Interests in the new array but not in DB.
    • To Remove: Interests in DB but not in the new array.
  • Use Drizzle to insert new links and delete old ones.

Sample Drizzle Code

// 1. Upsert interests
await db.insert(interests).values(
  newInterests.map(name => ({ name }))
).onConflictDoNothing(); // prevents duplicate interest names

// 2. Get interest IDs
const interestRecords = await db
  .select({ id: interests.id, name: interests.name })
  .from(interests)
  .where(in(interests.name, newInterests));

const interestIdMap = Object.fromEntries(
  interestRecords.map(rec => [rec.name, rec.id])
);

// 3. Get current user's interests
const currentLinks = await db
  .select({ interest_id: person_interests.interest_id })
  .from(person_interests)
  .where(eq(person_interests.person_id, personId));

const currentInterestIds = new Set(currentLinks.map(l => l.interest_id));
const newInterestIds = new Set(Object.values(interestIdMap));

// 4. Compute changes
const toAdd = [...newInterestIds].filter(id => !currentInterestIds.has(id));
const toRemove = [...currentInterestIds].filter(id => !newInterestIds.has(id));

// 5. Add new links
if (toAdd.length > 0) {
  await db.insert(person_interests).values(
    toAdd.map(interest_id => ({
      person_id: personId,
      interest_id,
    }))
  );
}

// 6. Remove old links
if (toRemove.length > 0) {
  await db.delete(person_interests)
    .where(
      and(
        eq(person_interests.person_id, personId),
        in(person_interests.interest_id, toRemove)
      )
    );
}

Why this approach?

  • Efficient: Only adds/removes what's changed.
  • Prevents duplicates.
  • Handles many-to-many properly.
  • Works with Drizzle and Postgres.

Tips

  • Wrap in a transaction for atomicity.
  • Validate input on the backend.
  • You do not update interests themselves, just the linking table.

If your interests are not a separate table (i.e., you store them as a JSON array), the approach is simpler (just update the array), but you'll lose normalization, searchability, and de-duplication features.

Let me know if you want the code adapted for a different schema or want transaction handling!

@a1300
Copy link
Author

a1300 commented Jul 13, 2025

@a1300
Copy link
Author

a1300 commented Jul 18, 2025

@a1300
Copy link
Author

a1300 commented Jul 18, 2025

@a1300
Copy link
Author

a1300 commented Jul 19, 2025

To type a generic ReactNode that guarantees a className prop, you’ll want to constrain the type to a React element that accepts props — particularly one that includes className. A plain ReactNode is too flexible (it can be a string, fragment, boolean, etc.), so you’ll need a more refined approach.

Here’s a pattern that works well:

import React, { ReactElement, ComponentType } from 'react';

type WithClassNameProps = {
  className?: string;
};

type Props<T extends ComponentType<WithClassNameProps>> = {
  Component: T;
};

// Example usage
const Wrapper = <T extends ComponentType<WithClassNameProps>>({
  Component,
}: Props<T>) => {
  return <Component className="my-class" />;
};

🔍 What’s happening here:

  • ComponentType<P> is a generic for a React component that accepts props of type P.
  • We're restricting it with WithClassNameProps so we know for sure it accepts className.
  • This ensures your passed-in Component (like a div, button, or custom component) will support the className prop at type level.

If you’re passing an actual element (like <div />) rather than a component reference, you’d use ReactElement<WithClassNameProps> instead.

Would you like an example with JSX-style children too? Or maybe you want to extend the prop checks even further?

@a1300
Copy link
Author

a1300 commented Jul 19, 2025

React.cloneElement is a powerful—but often misunderstood—tool in React’s arsenal. It lets you create a new React element based on an existing one, while overriding or adding props and children. Here's why and when you'd use it:

🧩 Why Use React.cloneElement()

  • Inject props into children: Useful when a parent component needs to modify or extend its children without changing their original implementation.
  • Enhance reusability: You can build flexible, reusable UI components by dynamically customizing child elements.
  • Avoid prop drilling: Instead of passing props through multiple layers, cloneElement lets you inject them directly.

📦 Common Use Cases

  • Customizing child components:

    const cloned = React.cloneElement(<Button />, { color: 'red' });

    This creates a new <Button /> with the color prop set to 'red'.

  • Dynamic rendering in lists:

    React.Children.map(children, (child, index) =>
      React.cloneElement(child, { isActive: index === selectedIndex })
    );

    This is handy for components like tabs or selectable lists.

  • Wrapping components with additional behavior:
    For example, adding event handlers or styling to children passed into a container.

⚠️ Caveats

  • Can make code harder to trace: Since props are injected dynamically, it’s not always obvious where they come from.
  • Not ideal for static typing: TypeScript may struggle with inferred props when using cloneElement.
  • Better alternatives often exist: Consider using render props, context, or custom hooks for clearer data flow.

Would you like to see a real-world example or explore alternatives like render props or context?

@a1300
Copy link
Author

a1300 commented Jul 19, 2025

@a1300
Copy link
Author

a1300 commented Jul 19, 2025

@a1300
Copy link
Author

a1300 commented Jul 20, 2025

@a1300
Copy link
Author

a1300 commented Aug 2, 2025

``
const [selection, setSelection] = useState({ state: {}, items: [] });

const table = useReactTable({
data,
columns,
getRowId: row => row.id,
state: { rowSelection: selection.state },
onRowSelectionChange: updater => {
const newState = updater(selection.state);
const selectedItems = data.filter(item => newState[item.id]);
setSelection({ state: newState, items: selectedItems });
},
});

@a1300
Copy link
Author

a1300 commented Aug 4, 2025

@a1300
Copy link
Author

a1300 commented Sep 7, 2025

@a1300
Copy link
Author

a1300 commented Sep 11, 2025

type OnlyArrays<T> = {
    [K in keyof T]: T[K] extends any[] ? K : never
}[keyof T]

type ArrayItemType<T> = T extends (infer U)[] ? U : never;

function access<
    T extends object,
    K extends OnlyArrays<T>,
    A extends (a: ArrayItemType<T[K]>) => boolean
>
    (first: T, key: K, func: A): {
        added: Array<ArrayItemType<T[K]>>
         
    } {
        const added = (first[key] as Array<ArrayItemType<T[K]>>).filter(func)

        return {
            added,
        }
}

access(obj, "addresses", (a) => true);

improvement

function access<
  T extends Record<K, any[]>,
  K extends keyof T,
  A extends (a: ArrayItemType<T[K]>) => boolean
>(
  first: T,
  key: K,
  func: A
): {
  added: ArrayItemType<T[K]>[]
} {
  const added = first[key].filter(func);
  return { added };
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment