# Understanding The React Source Code - UI Updating (Transaction) VI

To some extent, the sophisticated and efficient UI updating is what makes React React. But before we dive into the well-known mechanisms (virtual DOM and diffing algorithm) that empower the UI updating, we need to understand Transaction which transfers the control from the high-level API setState() to those underlying processing logic.

renderers/shared/utils/Transaction.js: defines the core Transaction class

renderers/shared/stack/reconciler/ReactDefaultBatchingStrategy.js: defines ReactDefaultBatchingStrategyTransaction and its API wrapper ReactDefaultBatchingStrategy

renderers/shared/stack/reconciler/ReactUpdates.js: defines the enqueueUpdate() that uses ReactDefaultBatchingStrategy

Unlike the previous posts that start from everyday APIs and move down the call stack. This post will take a bottom up approach.

So firstly, we look at the

# Transaction the core class

The only de facto “public” method of this class is perform that also offers its core functionality:

Besides the invocation of the callback method passed to it as the first argument, perform() simply 1) invokes initializeAll() before the callback and 2) closeAll() after.

Here the errorThrown is used to indicate an exception occurred within method.call(), in which case the logic jump directly to finally block before errorThrown can get a chance to be set to false.

Next we look at the implementation of the two methods that are invoked before and after perform(),

These two methods simply iterate this.transactionWrappers and call their initialize() and close() respectively.

The this.transactionWrappers is initialized in the de fecto constructor of Transaction with this.getTransactionWrappers():

We will see what exactly are those this.transactionWrappers very soon.

The exception handling detail here is a bit interesting. Take initializeAll() as an instance. In the case that an exception occurs within initialize(), a finally block (instead of a catch) processes the initialize() of the rest of this.transactionWrappers (i.e., from i + 1 to transactionWrappers.length-1). Then the exception interrupts the for loop and the entire initializeAll() logic and processes all the way to the finally block within perform(), the initializeAll()’s caller, which effectively skips

in the case of a exceptional initialization. At last, closeAll() is invoked within the same finally block to finalize the transaction.

Now we know what is a Transaction in its essence, but what is it used for? In order to answer this question, we take a Transaction instantiation as an example that is the transactional entry point of UI updating.

# ReactDefaultBatchingStrategyTransaction

Firstly ReactDefaultBatchingStrategyTransaction is a subclass of Transaction that implements getTransactionWrappers():

Next, TRANSACTION_WRAPPERS are the source of this.transactionWrappers that offers the pre (initialize()) and post (close()) functions for perform() used in the last section.

1) in the constructor of ReactDefaultBatchingStrategyTransaction the super class Transaction’s constructor gets called, which initializes this.transactionWrappers with FLUSH_BATCHED_UPDATES defined in 2)

2) defines the two wrapper and their respective initialize() and close(), which is used in the Transaction.initializeAll() and Transaction.closeAll() in the loops iterating FLUSH_BATCHED_UPDATES

3) defines ReactDefaultBatchingStrategyTransaction as a singleton.

Last we look at the public API offered by ReactDefaultBatchingStrategy that can be called from the outside world

ReactDefaultBatchingStrategy is injected {post two *5} to ReactUpdates as batchingStrategy. And the ReactDefaultBatchingStrategy.batchedUpdates() is used by ReactUpdates.enqueueUpdate(), the underlying method of the UI updating entry point setState().

Here is a similar recursion trick as we saw in last post.

1) When the method is entered the first time, ReactDefaultBatchingStrategy.isBatchingUpdates is false, which triggers branch {a} that leads to ReactDefaultBatchingStrategy.batchedUpdates();

2) batchedUpdates() sets ReactDefaultBatchingStrategy.isBatchingUpdates to true, and initializes a transaction;

3) the callback argument of batchedUpdates is enqueueUpdate() itself, so enqueueUpdate will be entered again with transaction.perform() straight away. Note that the pre-methods (initialize()) of both wrappers are emptyFunction so nothing happens between the two times invocation of enqueueUpdate();

4) when enqueueUpdate() is entered the second time (within the context of the transaction just initialized), branch {b} is executed;

5) after enqueueUpdate() returned post-method (close()) of FLUSH_BATCHED_UPDATES is called; This is the workhorse method that processes all the dirtyComponents marked in the previous step(s)

*8 we will come back to this FLUSH_BATCHED_UPDATES.close() and ReactUpdates.flushBatchedUpdates() in the next post

6) last, post-method (close()) of RESET_BATCHED_UPDATES is called, which sets ReactDefaultBatchingStrategy.isBatchingUpdates back to false and completes the circle.

It is important to note that any successive calls of enqueueUpdate() between 3) and 6) are supposed to be executed in the context of ReactDefaultBatchingStrategy.isBatchingUpdates:false, meaning, branch {b} will be taken in such case. So it’s like

# Wrap-up

That's it. Did I make a serious mistake? or miss out on anything important? Or you simply like the read. Link me on -- I'd be chuffed to hear your feedback.