Skip to content

Add a transform option to useListVals and useObjectVal in realtime database #91

@Satyam

Description

@Satyam

Sorry, I meant to provide a pull request but, though I did fork the repository and cloned it to my machine, doing an npm i produced errors which I was unable to fix. Since the change is short, I hope it is acceptable for me to send it this way.

Realtime database cannot handle anything that JSON cannot, dates being a case in point.

In order to simplify the use of such variables, I suggest adding a transform option along keyField in useListVals and useObjectVal.

Considering that such a transformation might be required at some point within the application anyway, doing it within the hook will have three main advantages:

  1. Avoid a new pass through the data while doing the conversion, saving on processing
  2. Avoid another memoizing of the results of the conversion, saving on memory
  3. Simplify the code for the developer

The first two would most significant with large lists of data. On the other hand, there is complete backward compatibility as omitting the proposed option would produce the same result as now.

This could be easily implemented in snapshotToData as shown below::

export const snapshotToData = (
  snapshot: database.DataSnapshot,
  keyField?: string,
  transform: (any) => any = value => value // <== added
) => {
  if (!snapshot.exists) {
    return undefined;
  }

  const val = snapshot.val();
  if (isObject(val)) {
    return {
      ...transform(val), // <== changed
      ...(keyField ? { [keyField]: snapshot.key } : null),
    };
  }
  return val;
};

And, of course, the declaration and passing through of the transform option from useListVals and useObjectVal to snapshotToData.

Then, it could be used like this:

type VentaType = {
  idVenta: string;
  fecha: Date; // <== it is already declared as type Date
  // ...Other fields
}

const options = {
  keyField: 'idVenta',
  transform: (data) => ({
    ...data,
    fecha: new Date(data.fecha),
  }),
};

export const useVenta: (
  idVenta: string
) => [VentaType | undefined, boolean, any] = (idVenta) =>
  useObjectVal<VentaType>(database.ref(`ventas/${idVenta}`), options);

export const useVentas: () => [
  Array<VentaType> | undefined,
  boolean,
  any
] = () =>
  useListVals<VentaType>(database.ref('ventas'), options);

The transformations might also allow merging or any other per-record manipulation, such as

  transform: (data) => ({
    ...data,
    year: new Date(data.fecha).getFullYear(),
  });

// or:

  transform: ({firstName, lastName, ...data}) => ({
    ...data,
    fullName: `${firstName} ${lastName}`,
  });

Additionally, a context option might also be added which would be passed to transform as a second argument, though I cannot really think of a good example which means, it is probably pointless. Perhaps running totals? Or merging in default values?

  transform: ({fecha, qty, ...data}, context) => {
    context.total += qty.
     return ({
    ...data,
    qty,
    year: new Date(fecha).getFullYear(),
    total: context.total
  });

// or

  transform: (data, context) => ({
    ...context,
    ...data,
  });

As I said, context might not be a good idea.

Thanks for your patience, it went on for longer than I thought.
Cheers

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions