firebase / firebase/firebase-js-sdk

FR: Provide way to leverage structural sharing in DataSnapshot

Open
#202 3 comments 0 reactions 1 assignee Assigned to @maneesht View on GitHub
api: database feature request
Dominant language
TypeScript
Stars
5.1k
Forks
1k
Avg merge
2d 21h
Merged PRs (30d)
37

Description

### [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()`](https://github.com/firebase/firebase-js-sdk/blob/3f827b82aa83cb6b5538bb7881f3253e9af0d15f/packages/database/src/api/DataSnapshot.ts#L87-L100), 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](https://reactjs.org/docs/optimizing-performance.html#avoid-reconciliation), 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.

```javascript
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.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.