Skip to content

Commit ff148d0

Browse files
committed
[middleware] DidSetRow guide
1 parent b7a2fa1 commit ff148d0

1 file changed

Lines changed: 65 additions & 4 deletions

File tree

‎site/guides/04_using_middleware.md‎

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ console.log(store.getValue('shopName'));
3333

3434
## Available Callbacks
3535

36-
A Middleware object supports 14 different callbacks that you can register. Each
36+
A Middleware object supports 15 different callbacks that you can register. Each
3737
callback is passed relevant parameters for the operation, and can return a
3838
value. The meaning of this return value depends on the type of callback:
3939

@@ -42,6 +42,9 @@ value. The meaning of this return value depends on the type of callback:
4242
If the function returns undefined (or void), the operation is cancelled.
4343
* For `willDel*` callbacks, the return value is a boolean that indicates whether
4444
the delete operation should proceed (true) or be cancelled (false).
45+
* For `didSetRow`, the return value is the final Row state to apply. Return
46+
`newRow` to accept, a different `Row` to replace, `oldRow` to revert, or an
47+
empty object to delete. See below for more details.
4548

4649
The full list of `willSet*` callbacks you can register is as follows:
4750

@@ -56,7 +59,16 @@ The full list of `willSet*` callbacks you can register is as follows:
5659
| willSetValue | valueId, value | When setValue is called. | Value or undefined |
5760
| willApplyChanges | changes | When applyChanges is called. | Changes or undefined |
5861

59-
The full list of `willDel*` callbacks you can register is as follows:
62+
There is also one special `did*` callback that is called at the end of the
63+
transaction - after all `will*` callbacks have settled and before any listeners
64+
are called. This gives you a chance to inspect or validate the final state of a
65+
Row for a specific table:
66+
67+
| Callback | Parameters | Called | Return |
68+
| --------- | ------------------------------ | ---------------------------------------------- | ------ |
69+
| didSetRow | tableId, rowId, oldRow, newRow | After all row changes settle in a transaction. | Row |
70+
71+
Finally, the full list of `willDel*` callbacks you can register is as follows:
6072

6173
| Callback | Parameters | Called | Return |
6274
| ------------- | ---------------------- | ------------------------- | ------- |
@@ -164,8 +176,8 @@ might prefer to use the more granular Cell and Value callbacks where possible.
164176
## Middleware And Listeners
165177

166178
Mutator listeners (that is, listeners registered with the `isMutator` flag set
167-
to `true`) are allowed to write data back to the Store during a transaction.
168-
Any writes made by a mutator listener will also pass through the middleware
179+
to `true`) are allowed to write data back to the Store during a transaction. Any
180+
writes made by a mutator listener will also pass through the middleware
169181
pipeline:
170182

171183
```js
@@ -187,6 +199,55 @@ console.log(store.getCell('pets', 'fido', 'slug'));
187199
In the example above, both the original `setCell` call and the listener's
188200
`setCell` call pass through the uppercase middleware.
189201

202+
## Post-Transaction Row Callback
203+
204+
The `will*` callbacks fire synchronously as each write happens. Sometimes you
205+
want to inspect or validate a Row *after* all the dust has settled — after every
206+
cell change in a transaction has been applied and the Row is in its final state.
207+
That's what `didSetRow` is for.
208+
209+
`addDidSetRowCallback` is **table-scoped**: you register it for a specific
210+
table, so there is zero overhead for tables you don't care about.
211+
212+
```js
213+
const store2 = createStore();
214+
const middleware2 = createMiddleware(store2);
215+
216+
middleware2.addDidSetRowCallback('pets', (tableId, rowId, oldRow, newRow) => {
217+
// Require 'species' — revert rows that don't have it
218+
return 'species' in newRow ? newRow : oldRow;
219+
});
220+
221+
store2.setRow('pets', 'fido', {species: 'dog', name: 'Fido'});
222+
console.log(store2.getRow('pets', 'fido'));
223+
// -> {species: 'dog', name: 'Fido'}
224+
225+
store2.setRow('pets', 'nemo', {name: 'Nemo'});
226+
console.log(store2.getRow('pets', 'nemo'));
227+
// -> {}
228+
```
229+
230+
The callback receives `oldRow` (the Row as it was at the start of the
231+
transaction), and `newRow` (the Row as it is now, after all cell writes).
232+
233+
The callback needs to return the Row that should be the final state of the Row
234+
after any corrections. Return:
235+
236+
- `newRow` to accept the changes.
237+
- a different `Row` to replace the final state.
238+
- `oldRow` to revert all changes to the Row.
239+
- an empty object to delete the Row.
240+
241+
Unlike `willSet*` callbacks, the `didSetRow` chain never short-circuits: all
242+
registered callbacks for the table always run, each receiving the Row returned
243+
by the previous one.
244+
245+
Because `didSetRow` fires *after* mutating listeners, `newRow` reflects the
246+
truly final state of the Row — including any writes those listeners made.
247+
248+
Also note that multiple cell changes to the same Row within one transaction
249+
produce only one `didSetRow` call, not one per cell.
250+
190251
## Middleware And Schemas
191252

192253
The callbacks are called after the schema has been applied to the data. So, for

0 commit comments

Comments
 (0)