How-to organize your cells for flexibility
Tags: redwoodjs, rwjs, javascript, graphql
Published: 2023-06-22 09:36:18


One of the early defining feature of #redwoodJS was Cells: React components designed to help you organise a Graphql's request lifecycle with frontend components for each of its states. RWJS is rather flexible, which leaves a lot of freedom for creative design and spectacular mistakes (I've had my share). Organising your code can become a challenge, so this post is meant to help you.


Over my past 3 years full time on RWJS I have been repeatedly confronted to the feeling that Cells were useful but were difficult to scale with. At the end of the day, I feel like the only real immutable part of a Cell, thus defining its nature, being the QUERY, there was no reason to feel constrained by a Cell's other parts, namely: 
```ts
{ QUERY, beforeQuery, afterQuery, isEmpty, Loading, Failure, Empty, Success, displayName, }
```

Most of the time the only thing I had to change was the Success component, which created the itch, but was not big enough to bring forward a clearer solution. I used `as` and `variant` props to change the `Success` component, but felt unsatisfied.



## Better variants with createCell
The following proposition is far from being perfect, but it's the one version that allowed me to feel confident in a design based on RedwoodJS, with minor tweaks to take cells to another level. I admit I might have been the only one in need of this and that it may be for lack of better engineering skills, but hey, it's a journey, we're all learning.





[createCell](https://github.com/redwoodjs/redwood/blob/main/packages/web/src/components/createCell.tsx) powers the transformation of your `XCell.tsx`. The framework exposes it, so you can reuse it and get creative.

To address my code organisation problem with a 'variant' solution, I'm fulling relying on `createCell`.


## Step by step

### Create variants
In the same directory as the cell you are working on, create a `variants` directory and add the following as a base for `index.ts`:
```ts
// ./variants/index.ts
import * as List from './List'
import * as Select from './Select'

export type Variants = 'list' | 'select'

export const variants = new Map<Variants, unknown>([
  ['list', List],
  ['select', Select],
])
```

This declares only two variants, let's keep things simple and light. The exported type `Variants` helps with the Cell's `variant` prop typing and `variants` is the map we will use in the default export.

#### Add the variant code
The imported modules, `List` and `Select`, should have at least one Cell part different from what we have defined in our original cell. In my case, I neutralized the `Loading`, `Failure` and `Empty` components in the `Select` variant and added a different `afterQuery` in the `List` variant.
I didn't do it, but a better way to handle the specificity and complexity here would be to rely on `yarn rw g cell X` to create the boilerplate code for each variant. You'd be set with tests & stories upfront.

### Neutralize the import transformer
Back in the cell's directory, rename your `XCell.tsx` to drop the `Cell` suffix, otherwise you'll have import conflicts. This remains your entry point and should have all your default Cell parts.

### Activate variants with default support
Your default export in this file should look like the following:
```tsx
type Params = {
  random: string
}

export default React.memo(function ({ variant, ...props }: {
  variant: Variants
} & CellSuccessProps<MyQuery, Exact<Params>>) {
  return createCell<
    CellSuccessProps<MyQuery, MyQueryVariables>,
    MyQueryVariables
  >({
    QUERY,
    afterQuery,
    Loading,
    Failure,
    Empty,
    Success,
    ...(variant ? variants.get(variant) : {}),
  })(props)
})
```

That's about it.

Now the reason I prefer this over the Cell boilerplate is that the generated code helped me develop fast but on longer term I found myself more confused when facing other cases for my cells, as I didn't necessarily anticipate the need for a cell to be way more flexible. For a long time I thought only working on a different `Success` component was enough - and in some way it is, but enabling the entire module to be altered in variants is helping me **pushing design and attention to details a lot further**.
Another solution would be to generate the same cell with a different name and the same query over and over, but this rings to me as a trap to bloat the codebase and face more and more the dreaded problem of naming things.

So I'll stick to this `variants` implementation.