npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-firebase-mirror

v0.0.12

Published

A library to help you easily mirror firebase data inside a redux store.

Downloads

5

Readme

CircleCI codecov npm npm styled with prettier

redux-firebase-mirror

A library to help you easily mirror firebase data inside a redux store, and use that data inside a react application.

Table of Contents

  1. Installation
  2. Set Up
  3. API
    1. Configuration
    2. Action Creators
    3. Selectors
    4. Subscriptions
    5. React Higher-Order-Components

Installation

npm install redux-firebase-mirror redux redux-thunk firebase

Peer Dependencies

redux-firebase-mirror has the following peer and optional dependencies:

  • redux - this should be obvious. This library is designed to work with redux so you need to have it installed for this to be of any use.
  • firebase - Another obvious peer dependency. This library talks directly to firebase to fetch data.
  • redux-thunk - You must set up your redux store with the redux-thunk middleware which this library uses to execute asynchronous redux actions.

Set Up

Data from firebase will be mirrored in your redux state tree. For this to work, you must incorporate redux-firebase-mirror's redux reducer into your project's top-level redux reducer.

Use the combineReducers() utility function from redux to place the reducer under the firebaseMirror key. Here is an example of what this might look like:

import reduxFirebaseMirror from 'redux-firebase-mirror';
import {combineReducers, createStore, applyMiddleware} from 'redux';
import thunkMiddleware from 'redux-thunk';

const app = firebase.initializeApp({...});
const firebaseMirror = reduxFirebaseMirror({
  getFirebase: () => app
});

const store = createStore(
  combineReducers({
    firebaseMirror,
    // your applications other reducers here...
  }),
  applyMiddleware(thunkMiddleware)
);

API

Configuration

reduxFirebaseMirror(config)

This function is used to initialize and configure the redux-firebase-mirror module and get a reducer to incorporate in your applications exisiting redux store. It takes an optional config object for customizing behavior.

Param | Type | Description --------------|------|----------------------------------------------- config | Object | configuration object. See options below. config.getFirebase | Function | function for getting the firebase App instance to use. config.getFirebaseState | Function | Optional selector function for getting the state used by redux-firebase-mirror. If not specified, will default to (state) => state.firebaseMirror; config.persistToLocalStorage | Object | A config object for persisting the mirror to local storage. If not provided, no data will be persisted. See below for the specific options config.persistToLocalStorage.storage | localStorage\|sessionStorage | Optionally specify a custom storage system. Defaults to using localStorage. Can be any object which implements the same API as localStorage. Also supports async storage apis such as react native's AsyncStorage. config.persistToLocalStorage.storagePrefix | string | Optionally specify a prefix to use for all keys given to localStorage. Defaults to "firebase-mirror:". config.syncInterval | number | The minimum number of milliseconds between syncs to the redux store. Defaults to 30

returns: a redux reducer function


Action Creators

The following action creators can be used to control what parts of the firebase database are being mirrored.

subscribeToValues(paths)

Registers listeners for firebase value events at the specified paths. Whenever the value changes in firebase, the redux store will be updated.

Param | Type | Description ------|------|------------ paths | PathSpec[] | The array of firebase database paths to subscribe to. See the PathSpec documentation below for more details on how to configure filtering and ordering of the results returned by firebase.

returns: nothing

Example:

store.dispatch(subscribeToValues([
  `/profiles/${userId}`,
  `profilePics/${userId}/square`
]));
PathSpec

A PathSpec is either a string specifying the exact path to a location in the firebase database or an object containing a path along with additional configuration information. The configuration object has the following shape:

Param | Type | Description ------|------|------------ path | string | the path to load from firebase orderByKey | boolean | If true, results will be ordered by the key orderByValue | boolean | If true, results will be ordered by value orderByChild | string | If given, results will be ordered by the value of the child with the given key filter | Object | See further configuration options below filter.limitToLast | number | results will be limited to the last N values filter.limitToFirst | number | results will be limited to the first N values filter.startAt | number | results will start at the given index filter.endAt | number | results will end at the given index filter.equalTo | any | results will match the given value

For example, you can use PathSpec configuration objects to subscribe to the last 10 messages in an inbox for a given user:

subscribeToValues([
  {
    path: '/messages',
    orderByChild: 'sentTo',
    filter: {
      equalTo: userId,
      limitToLast: 10,
    },
  }
]);

unsubscribeFromValues(paths)

Stops listening to firebase value events at the specified paths. The mirror will no longer be updated at this location unless there is another listener at a higher path. Note that whatever data was already mirrored will still be available in the redux state.

Param | Type | Description ------|------|------------ paths | string[] | The array of firebase database paths to subscribe to.

returns: nothing

Example:

store.dispatch(unsubscribeFromValues([
  `/profiles/${userId}`,
  `profilePics/${userId}/square`
]));

fetchValues(paths, callback)

Mirrors the values at the specified paths in the firebase database into your redux store once. The mirrored data will not continue to sync with firebase.

Param | Type | Description ------|------|------------ paths | string[] | The array of firebase database paths to fetch. callback | [Function] | Optional callback function which will be called once the data has been synced.

returns: a Promise that resolves once the data has been synced.

Example:

store.dispatch(fetchValues([
  `/profiles/${userId}`,
  `profilePics/${userId}/square`
])).then(() => {
  console.log("received profile data", getValueAtPath(store.getState(), `/profiles/${userId}`));
  console.log("profile picture is", getValueAtPath(store.getState(), `profilePics/${userId}/square`));
});

Selectors

The following selector functions can be used to query the mirror.

getValueAtPath(state, path)

Returns the value at the given firebase database path, as it was most recently mirrored, as an Immutable object.

Param | Type | Description ------|------|------------ state | Redux State | The redux store's state, i.e. store.getState() path | string | The firebase database path to get.

Returns: undefined | boolean | number | string | Immutable.List | Immutable.Map - The immutable value at the given path as an Immutable object, or undefined if there is nothing there.

Example:

const userProfile = getValueAtPath(store.getState(), '/profiles/${userId}');
console.log("user is", userProfile.get("name"));

getKeysAtPath(state, path)

Similar to getValueAtPath, but only returns an array of keys at the path.

Param | Type | Description ------|------|------------ state | Redux State | The redux store's state, i.e. store.getState() path | string | The firebase database path to get.

Returns: string[] - The list of keys at the given database path.

Example:

const userIds = getKeysAtPath(store.getState(), '/profiles');
userIds.forEach(userId => {
  console.log(
    "user", userId,
    "is named", getValueAtPath(store.getState(), `/profiles/${userId}`).get("name"),
  );
});

Subscriptions

To make subscribing to and querying the same set of paths all over the place easier, you can use the Subscription class.

new Subscription({paths, value})

Creates a new Subscription object which describes how to fetch and interpret values in the database.

Param | Type | Description ------|------|------------ config | Object | A configuration object for the subscription. See options below. config.paths | Function | A function that maps state and props to an array of paths or PathSpec config objects. config.value | Function | A function that maps state and props to a particular value in the database.

Example:

const profileById = new Subscription({
  paths: (state, props) => [`/profiles/${props.userId}`],
  value: (state, props) => getValueAtPath(state, `/profiles/${props.userId}`),
});

Example of a fanout query:

const friendIds = new Subscription({
  paths: profileById.paths,
  value: (state, props) => getKeysAtPath(state, `/profiles/${props.userId}/friends`),
});

const profilePicById = new Subscription({
  paths: (state, props) => [`/profilePics/${props.userId}/${props.size}`],
  value: (state, props) => (
    getValueAtPath(state, `/profilePics/${props.userId}/${props.size}`) ||
    '/static/silhouette.png'
  )
});

const friendProfilePics = new Subscription({
  paths: (state, props) => [
    ...friendIds.paths(state, props)];
    ...friendIds.value(state, props).map(
      friendId => profilePicById.path(state, {userId: friendId, size: props.size})
    )
  ],
  value: (state, props) => {
    return friendIds.value(state, props).map(
      friendId => profilePicById.value(state, {userId: friendId, size: props.size})
    );
  }
});

Subscription.fetchNow(store, props)

Will fetch all the data needed by the subscription (filling the mirror in the process) and resolve the returned promise with the subscription's resulting value.

Param | Type | Description ------|------|------------ store | Redux State | The redux store. props | Object | Whatever props are needed by the subscription's paths and value functions.

Returns: a Promise that resolves to whatever the value function returns.

Example:

friendProfilePics
  .fetchNow(store, {userId: getLoggedInUser(store.getState()), size: 'small'})
  .then(urls => {
    console.log("profile pictures of your friends can be found at the following urls:", urls.join('\n'));
  });

Subscription.mapProps(mapper)

Creates a new subscription object that can handle props with different names. Useful in conjunction with the react higher order components.

Param | Type | Description ------|------|------------ mapper | Function | A function that translates from one prop format to another.

returns: a new Subscription instance.

Example:

// let me pass in userId as id instead
friendProfilePics.mapProps(({id}) => ({userId: id}));

React Higher-Order-Components

subscribePaths(mapPropsToPaths)

Wraps a react component such that the given paths are subscribed to when the component is mounted.

Param | Type | Description ------|------|------------ mapPropsToPaths | Function | A function that maps the current redux state and the component's props to an array of paths.

Example:

const FundraisingMeter = compose(
  subscribePaths(
    (state, props) => ['/fundraising'],
  ),
  connect(
    (state, props) => ({fund: getValueAtPath(state, '/fundraising')})
  )
)(class extends Component {
  render() {
    if (!this.props.fund) {
      return <span>Loading...</span>;
    }
    const remaining = this.props.fund.get("goal") - this.props.fund.get("current");
    return (
      <span>
        We are ${remaining} dollars away from our goal!
      </span>
    );
  }
});

subscribeProps(mapPropsToSubscriptions)

Similar to subscribePaths, but takes a mapping from props to a collection of subscription objects and populates the given components props with the values from the subscriptions.

Param | Type | Description ------|------|------------ mapPropsToSubscriptions | Function\|Object | A function that maps the current redux state and the component's props to an array of paths. Alternatively, you can also just pass in an object if you don't care about the state or props.

class ImageCollage extends Component {
  static propTypes = {
    imgUrls: PropTypes.arrayOf(PropTypes.string),
  };
  render() {
    return (
      <div>
        {this.props.imgUrls.map(imgUrl => <img src={imgUrl} />)}
      </div>
    );
  }
}

const FriendCollageContainer = subscribeProps({
  imgUrls: friendProfilePics,
})(ImageCollage);

Rehydration from stored state

If you are using redux-firebase-mirror in an isomorphic react application with subscriptions occuring on both the client and the server, then you will probably want to populate the client state with data already fetched by the server. You can do this using the following selector and action:

getDehydratedState(state)

Returns a serializable version of the mirrored state used by redux-firebase-mirror.

Param | Type | Description ------|------|------------ state | Redux State | The redux store's state, i.e. store.getState()

rehydrate(data)

This action creator will update the store with the rehydrated data.

Param | Type | Description ------|------|------------ data | Object | The data with which to rehydrate the state. i.e. the result of getDehydratedState

See more information about state rehydration in redux in the redux documetation.