Skip to content

FR: Provide way to leverage structural sharing in DataSnapshot #202

Open
@dtinth

Description

@dtinth

[REQUIRED] Describe your environment

  • Operating System version: Mac OS X 10.12
  • Firebase SDK version: 4.1.1
  • Firebase Product: database

[REQUIRED] Describe the problem

In React-based apps, we optimize the performance of the app with “structural sharing.”

Since a DataSnapshot is immutable, I would expect a DataSnapshot of an unchanged child to be the same object in previous snapshot.

Steps to reproduce:

Consider the state of the realtime database where task2.title changed:

// s1 and s2 are `DataSnapshot` objects representing a snapshot of at same `Reference` at different point in time.
s1 -> { task1: { title: 'A' }, task2: { title: 'B' } }
s2 -> { task1: { title: 'A' }, task2: { title: 'Z' } }

Since between these 2 snapshots, the data of task1 child is unchanged. Therefore, I would expect that:

s1.child('task1') === s2.child('task1')

However, a new DataSnapshot is created every time you call .child(), hence, the above code yields false.

A workaround is to compare the underlying node:

s1.child('task1').node_ === s2.child('task1').node_

But it does not work with the hosted Firebase SDK as it gets closure-compiled, and node_ was renamed to A.

Having structural sharing makes it much easier to optimize a React application, by leveraging PureComponent and shouldComponentUpdate.

Relevant Code:

The workaround I am currently using is to parse the source code of dataSnapshot.val() at runtime to obtain access to the underlying node object.

function getUnderlyingNode (dataSnapshot) {
  // ULITAME HACK:
  //     PARSE THE FIREBASE SDK SOURCE CODE TO FIGURE OUT HOW
  //     TO OBTAIN THE UNDERLYING NODE.
  const match = dataSnapshot.val.toString().match(/return\s+this\.(\w+)/)
  return dataSnapshot[match[1]]
}

By comparing the underlying node_, I was able to improve the performance of my React app by ~100x. (It works with a lot of data.)

API suggestions:

To avoid such ugly hack, here are some ways that the DataSnapshot API can be improved so that userland code could leverage structural sharing:

  • Providing access to the underlying node. This node should be treated as an opaque object, but can be used as, e.g., a cache key for WeakMap to perform snapshot memoization.

  • Providing a method, e.g. dataSnapshot.frozenVal() that returns a frozen object/array with structural sharing.

  • Providing a method, e.g. dataSnapshot.immutableVal() that returns an Immutable.Map with structural sharing.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions