@@ -126,6 +126,67 @@ technically being updated. But the key is to think about the actual method that
126126was called on the Store, and then expect callbacks for only more granular
127127elements from there.
128128
129+ ## Complex Object Callbacks
130+
131+ Callbacks for Cell and Value operations (` willSetCell ` , ` willSetValue ` ) receive
132+ primitive values directly. But callbacks for Row, Table, Tables, Content, and
133+ Changes operations receive more complex objects — and the Store passes a deep
134+ clone of the original data to the callback, not the original itself.
135+
136+ This means you can safely mutate the received object in place and return it, or
137+ you can return a completely new object. Both approaches work:
138+
139+ ``` js
140+ // Approach 1: return a new object
141+ middleware .addWillSetRowCallback ((tableId , rowId , row ) => {
142+ return {... row, validated: true };
143+ });
144+
145+ // Approach 2: mutate in place and return
146+ middleware .addWillSetRowCallback ((tableId , rowId , row ) => {
147+ row .validated = true ;
148+ return row;
149+ });
150+ ```
151+
152+ Because the callback receives a clone, the original data passed to the Store
153+ method is never mutated by the middleware regardless of which approach you use.
154+
155+ When middleware transforms a rich object (i.e. returns something other than what
156+ it received), that change is applied as a mutation. This ensures the correct
157+ reactive behavior: listeners will see the transformed data, and the change will
158+ be tracked as a write in MergeableStore synchronization.
159+
160+ Note that there is a small cost to the cloning and mutation tracking for these
161+ more complex types of callbacks, so if you have performance-sensitive code, you
162+ might prefer to use the more granular Cell and Value callbacks where possible.
163+
164+ ## Middleware And Listeners
165+
166+ 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
169+ pipeline:
170+
171+ ``` js
172+ middleware .addWillSetCellCallback ((_tableId , _rowId , _cellId , cell ) =>
173+ typeof cell === ' string' ? cell .toUpperCase () : cell,
174+ );
175+
176+ store .addCellListener (' pets' , ' fido' , ' name' , (store ) => {
177+ store .setCell (' pets' , ' fido' , ' slug' , ' from_listener' );
178+ }, true );
179+
180+ store .setCell (' pets' , ' fido' , ' name' , ' Rex' );
181+ console .log (store .getCell (' pets' , ' fido' , ' name' ));
182+ // -> 'REX'
183+ console .log (store .getCell (' pets' , ' fido' , ' slug' ));
184+ // -> 'FROM_LISTENER'
185+ ```
186+
187+ In the example above, both the original ` setCell ` call and the listener's
188+ ` setCell ` call pass through the uppercase middleware.
189+
129190## Middleware And Schemas
130191
131192The callbacks are called after the schema has been applied to the data. So, for
@@ -167,7 +228,7 @@ Middleware and schemas, and use that power wisely!
167228
168229## When Middleware Is Not Called
169230
170- There are two main cases where Middleware callbacks will not be called:
231+ There are two specific cases where Middleware callbacks will not be called:
171232
172233* When ` doRollback ` returns true at the end of a Store transaction: the
173234 transaction will be rolled back and no callbacks will be called on the changes
0 commit comments