Communication between components wrapped into cells

Hey! I’m getting my head around the Cells and had to deal with a use case where I’m not really content with the solution. Here it is:

I have a classical example of a master-detail view - a grid displaying users and a form for editing a single user. The grid and the form are sibling components wrapped into cells. The cells fetch data for the corresponding components - list of users for the grid and required data for the form (available roles, departments, etc.).

Once the form is successfully submitted I need to refresh data in the grid. But since I cannot pass props directly through the cell components (at least I couldn’t find how to do this) I had to find a way to communicate this event indirectly.

I ended up adding two contexts:

  1. one for communication between the form and the parent component that hosts both - the form and the grid.
  2. second for communication between the parent and the grid components.

(Both of these components can be used on other pages, I didn’t want to couple them to this particular page, so instead of one shared context I had to introduce two.)

Everything is working fine, but the solution seems an overkill and I feel that I must be missing something obvious. I would really appreciate, If anyone can suggest any alternative approach or provide an opinion on this one. Thank you!

Hey again Anton - happy holidays this time!

I’m assuming you’re using the Cell for the data-fetching part of the equation, and then making use of useMutation to actually update the data, once the form is submitted.

The useMutation hook has a second argument, an options object, which accepts a refetchQueries field. If you want to pass props to the grid-cell, which are the same as passing variables to its GraphQL query, you can do so through refetchQueries by passing it an object - instead of a DocumentNode or the name of the query (see linked docs).

// I believe @redwoodjs/web also exports this hook - I just always use Apollo's.
// Better types :)
import { useMutate } from '@apollo/client'

import { QUERY } from 'src/components/MyCell'

export const MyForm = () => {
  const [mutation, ...] = useMutation(MUTATION, {
    refetchQueries: [{ query: QUERY, variables: { ...cell_props } }]
  })
}

As for getting which user you want to update, I typically delegate that responsibility to the Page component. My most recent pattern was passing the cell the results of a useState hook, and then giving the form-component the current value. The cell can then update the state, using the setter, whenever the edit button is clicked - for example.

Hopefully this is up the alley you were expecting!

1 Like

Thank you @realStandal for your help again :wink: !

Indeed, I was using refetch (injected into the Success cell component though), but I didn’t know that Cell variables also carry the Cell props and not only the query variables. Another thing that was not obvious to me - how to make it work with TypeScript. Here is what I ended up doing, though eventually I don’t even need to use Cell variables:

type CustomSuccessProps = {
  shouldRefetch: boolean,
  setShouldRefetch: (value: boolean) => void
}

type SuccessProps = CellSuccessProps<UsersQuery & CustomSuccessProps>

export const Success = ({ users: initial, refetch, shouldRefetch, setShouldRefetch }: SuccessProps) => {
  const [users, setUsers] = useState(initial)

  useEffect(() => {
    if (shouldRefetch) {
      setShouldRefetch(false)
      refetch().then(({ data }) => setUsers(data.users))
    }
  }, [shouldRefetch, setShouldRefetch, setUsers, refetch])

  return <UsersGrid users={users} />
}

Thank you again!
Happy Holidays!