Giter VIP home page Giter VIP logo

react-redux-firebase's Introduction

react-redux-firebase

NPM version NPM downloads Quality Code Coverage Code Style License Build Status

Gitter

Redux bindings for Firebase. Includes Higher Order Component (HOC) for use with React.

Usage Note

If you are starting a new project and/or are not required to have your Firebase data loaded into redux, you might want to give reactfire a try before trying react-redux-firebase. I wrote up a quick medium article explaining a bit about how, why, and showing how to start a new project with these tools.

Edit Simple Example

The Material Example is deployed to demo.react-redux-firebase.com.

Features

  • Out of the box support for authentication (with auto loading user profile from database/firestore)
  • Full Firebase Platform Support Including Real Time Database, Firestore, and Storage
  • Automatic binding/unbinding of listeners through React Hooks (useFirebaseConnect, useFirestoreConnect) or Higher Order Components (firebaseConnect and firestoreConnect)
  • Population capability (similar to mongoose's populate or SQL's JOIN)
  • Support small data ( using value ) or large datasets ( using child_added, child_removed, child_changed )
  • Multiple queries types supported including orderByChild, orderByKey, orderByValue, orderByPriority, limitToLast, limitToFirst, startAt, endAt, equalTo
  • Tons of examples of integrations including redux-thunk and redux-observable
  • Server Side Rendering Support
  • react-native support using native modules or web sdk

Installation

npm install --save react-redux-firebase

This assumes you are using npm as your package manager.

If you're not, you can access the library on unpkg, download it, or point your package manager to it. Theres more on this in the Builds section below.

Older Versions

Interested in support for versions of react-redux before v6 or the new react context API? Checkout the v2.*.* versions (installed through npm i --save react-redux-firebase^@2.5.0).

Use

Include firebaseReducer (reducer) while creating your redux store then pass dispatch and your firebase instance to ReactReduxFirebaseProvider (context provider):

import React from 'react'
import { render } from 'react-dom'
import { Provider } from 'react-redux'
import firebase from 'firebase/app'
import 'firebase/database'
import 'firebase/auth'
// import 'firebase/firestore' // <- needed if using firestore
// import 'firebase/functions' // <- needed if using httpsCallable
import { createStore, combineReducers, compose } from 'redux'
import {
  ReactReduxFirebaseProvider,
  firebaseReducer
} from 'react-redux-firebase'
// import { createFirestoreInstance, firestoreReducer } from 'redux-firestore' // <- needed if using firestore

const fbConfig = {}

// react-redux-firebase config
const rrfConfig = {
  userProfile: 'users'
  // useFirestoreForProfile: true // Firestore for Profile instead of Realtime DB
}

// Initialize firebase instance
firebase.initializeApp(fbConfig)

// Initialize other services on firebase instance
// firebase.firestore() // <- needed if using firestore
// firebase.functions() // <- needed if using httpsCallable

// Add firebase to reducers
const rootReducer = combineReducers({
  firebase: firebaseReducer
  // firestore: firestoreReducer // <- needed if using firestore
})

// Create store with reducers and initial state
const initialState = {}
const store = createStore(rootReducer, initialState)

const rrfProps = {
  firebase,
  config: rrfConfig,
  dispatch: store.dispatch
  // createFirestoreInstance // <- needed if using firestore
}

// Setup react-redux so that connect HOC can be used
function App() {
  return (
    <Provider store={store}>
      <ReactReduxFirebaseProvider {...rrfProps}>
        <Todos />
      </ReactReduxFirebaseProvider>
    </Provider>
  )
}

render(<App />, document.getElementById('root'))

The Firebase instance can then be grabbed from context within your components (withFirebase and firebaseConnect Higher Order Components provided to help):

Add Data

import React from 'react'
import { useFirebase } from 'react-redux-firebase'

export default function Todos() {
  const firebase = useFirebase()

  function addSampleTodo() {
    const sampleTodo = { text: 'Sample', done: false }
    return firebase.push('todos', sampleTodo)
  }

  return (
    <div>
      <h1>New Sample Todo</h1>
      <button onClick={addSampleTodo}>Add</button>
    </div>
  )
}

Load Data (listeners automatically managed on mount/unmount)

import React from 'react'
import PropTypes from 'prop-types'
import { useSelector } from 'react-redux'
import { useFirebaseConnect, isLoaded, isEmpty } from 'react-redux-firebase'

export default function Todos() {
  useFirebaseConnect([
    'todos' // { path: '/todos' } // object notation
  ])

  const todos = useSelector((state) => state.firebase.ordered.todos)

  if (!isLoaded(todos)) {
    return <div>Loading...</div>
  }

  if (isEmpty(todos)) {
    return <div>Todos List Is Empty</div>
  }

  return (
    <div>
      <ul>
        {Object.keys(todos).map((key, id) => (
          <TodoItem key={key} id={id} todo={todos[key]} />
        ))}
      </ul>
    </div>
  )
}

Queries Based On Route Params

It is common to make a detail page that loads a single item instead of a whole list of items. A query for a specific Todos can be created using

import React from 'react'
import PropTypes from 'prop-types'
import { get } from 'lodash'
import { useSelector } from 'react-redux'
import { useFirebaseConnect, useFirebase } from 'react-redux-firebase'
import { useParams } from 'react-router-dom'

export default function Todo() {
  const { todoId } = useParams() // matches todos/:todoId in route
  const firebase = useFirebase()

  useFirebaseConnect([
    { path: `todos/${todoId}` } // create todo listener
    // `todos/${todoId}` // equivalent string notation
  ])

  const todo = useSelector(
    ({ firebase: { data } }) => data.todos && data.todos[todoId]
  )

  function updateTodo() {
    return firebase.update(`todos/${params.todoId}`, { done: !todo.isDone })
  }

  return (
    <div>
      <input
        name="isDone"
        type="checkbox"
        checked={todo.isDone}
        onChange={updateTodo}
      />
      <span>{todo.label}</span>
    </div>
  )
}

Load Data On Click

import React from 'react'
import { useSelector } from 'react-redux'
import { useFirebase, isLoaded, isEmpty } from 'react-redux-firebase'

function TodosList() {
  const todos = useSelector((state) => state.firebase.ordered.todos)

  if (!isLoaded(todos)) {
    return <div>Loading...</div>
  }

  if (isEmpty(todos)) {
    return <div>Todos List Is Empty</div>
  }

  return (
    <ul>
      {Object.keys(todos).map((key, id) => (
        <TodoItem key={key} id={id} todo={todos[key]} />
      ))}
    </ul>
  )
}

export default function Todos() {
  const firebase = useFirebase()

  return (
    <div>
      <h1>Todos</h1>
      <EnhancedTodosList />
      <button onClick={() => firebase.watchEvent('value', 'todos')}>
        Load Todos
      </button>
    </div>
  )
}

Firestore

If you plan to use Firestore, you should checkout redux-firestore. It integrates nicely with react-redux-firebase and it allows you to run Real Time Database and Firestore along side each other.

react-redux-firebase provides the firestoreConnect HOC (similar to firebaseConnect) for easy setting/unsetting of listeners.

Currently react-redux-firebase still handles auth when using redux-firestore - The future plan is to also have auth standalone auth library that will allow the developer to choose which pieces they do/do not want.

See full documentation at react-redux-firebase.com

Examples folder is broken into two categories snippets and complete. /complete contains full applications that can be run as is, where as /snippets contains small amounts of code to highlight specific functionality (dev tools and deps not included).

Snippet showing querying based on data in redux state. One of the more common examples is querying based on the current users auth UID.

Snippet showing how to use decorators to simplify connect functions (redux's connect and react-redux-firebase's firebaseConnect)

A simple example that was created using create-react-app's. Shows a list of todo items and allows you to add to them.

An example that user Material UI built on top of the output of create-react-app's eject command. Shows a list of todo items and allows you to add to them. This is what is deployed to redux-firebasev3.firebaseapp.com.

Discussion

Join us on the redux-firebase gitter.

Integrations

View docs for recipes on integrations with:

Starting A Project

Generator

generator-react-firebase is a yeoman generator uses react-redux-firebase when opting to include redux.

CRA Template

cra-template-rrf is a create-react-app template with react-redux-firebase included

Complete Examples

The examples folder contains full applications that can be copied/adapted and used as a new project.

FAQ

Please visit the FAQ section of the docs

Builds

Most commonly people consume Redux Firestore as a CommonJS module. This module is what you get when you import redux in a Webpack, Browserify, or a Node environment.

If you don't use a module bundler, it's also fine. The redux-firestore npm package includes precompiled production and development UMD builds in the dist folder. They can be used directly without a bundler and are thus compatible with many popular JavaScript module loaders and environments. For example, you can drop a UMD build as a <script> tag on the page. The UMD builds make Redux Firestore available as a window.ReactReduxFirebase global variable.

It can be imported like so:

<script src="../node_modules/react-redux-firebase/dist/react-redux-firebase.min.js"></script>
<script src="../node_modules/redux-firestore/dist/redux-firestore.min.js"></script>
<!-- or through cdn: <script src="https://unpkg.com/react-redux-firebase@latest/dist/react-redux-firebase.min.js"></script> -->
<!-- or through cdn: <script src="https://unpkg.com/redux-firestore@latest/dist/redux-firestore.min.js"></script> -->
<script>
  console.log('react redux firebase:', window.ReactReduxFirebase)
</script>

Note: In an effort to keep things simple, the wording from this explanation was modeled after the installation section of the Redux Docs.

Contributors

This project exists thanks to all the people who contribute.

react-redux-firebase's People

Contributors

0x80 avatar anotherstarburst avatar bojhan avatar codedpills avatar danleavitt0 avatar dannyvaughton avatar dedan avatar dependabot[bot] avatar dirathea avatar dman757 avatar fej-snikduj avatar gregfenton avatar harish-aka-shivi avatar illuminist avatar javamonn avatar jenglamlow avatar karltaylor avatar lesmo avatar martynovs avatar meksikann avatar mrshll avatar prescottprue avatar rscotten avatar rusakovic avatar shalinit3 avatar ssdns avatar tapped avatar theashguy avatar urbantumbleweed avatar wwwmarcos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-redux-firebase's Issues

enableRedirectHandling option ignored?

Hi! For a project I'm working on I would like to manually handle the auth state and profile creation/updates. I have therefore tried using the config below to disable most of these features.

However, when using this configuration the middleware still seem to interfere with my own getRedirectResult handler. When plugging in this middleware the result to my getRedirectResult handler is always { user: null }, but when removing the middleware it works as expected.

I suspect it is because somehow the enableRedirectHandling-option is ignored, but it's only my best guess.

Any ideas?

const reduxFirebaseConfig = {
  updateProfileOnLogin: false,
  enableRedirectHandling: false,
  autoPopulateProfile: false,
  setProfilePopulateResults: false,
  userProfile: null,
  enableLogging: false,
}

const createStoreWithFirebase = compose(
  reactReduxFirebase(
    config.firebase.config,
    reduxFirebaseConfig
  ),
)(createStore)

export default createStoreWithFirebase(
  rootReducer,
  applyMiddleware(epicMiddleware),
)

How to work with state outside of firebase, redux

This repo is amazing, thanks so much for the work!

I'm a bit confused how to include non-firebase state into redux when using these connect methods.

Here's the example code:

const wrappedTodos = firebaseConnect([
  '/todos'
])(Todos)

export default connect(
  ({firebase}) => ({
    todos: dataToJS(firebase, 'todos'),
  })
)(wrappedTodos)

This generates a redux store with
firebase > data > todos

My question is how to manage store and connect()-ing when using things outside of firebase.

For example:

firebase > data > todos
activeTodo
otherStateToManage

Do you have any advice on including activeTodo and otherStateToManage in the connect() method?

Dealing with multiple queries on the same route

I would like to make a couple of queries to the same path, but with different params. However, these queries are stored in the store based on 'path' param.

Example:

@firebaseConnect((props) => ([
  { path: 'goals', queryParams: [ `orderByChild=partner`, `equalTo=partnerId`] },
  { path: 'goals', queryParams: [ `orderByChild=expert`, `equalTo=expertId`] },
]))
@connect(({ firebase }, props) => ({
  ???
}))

Would it be possible to name a path for the queries to be stored at?

Example of the feature:

@firebaseConnect(() => ([
  { path: 'goals', storeAs: 'partnerGoals', queryParams: [ `orderByChild=partner`, `equalTo=partnerId`] },
  { path: 'goals', storeAs: 'expertGoals', queryParams: [ `orderByChild=expert`, `equalTo=expertId`] },
]))
@connect(({ firebase }) => ({
  partnerGoals: helpers.dataToJS(firebase, 'partnerGoals'),
  expertGoals: helpers.dataToJS(firebase, 'expertGoals'),
}))

I think this solution would also help with issue #55 whereas I could just store the data under different paths, so they would not be mixed and I would save some calls to the database.

Ideas?

Edit: Checked the source and I see you have dest variable ( link ), but I couldn't find how it's used.

feat(populate): nested populates

Hello there,

I have a data structure as follows

obj1 = {
   "Achildren": ["childobj1", "childobj2", "childobj3" ....],
   "Bchildren": ["childobj4", "childobj5", "childobj6" ....],
}

Achildren = {
   "childobj1": {
       "attr1": "val1",
       "attr2": "val2",
       "innerChildren": ["inchild1', ''inchild2', ''inchild3' .....]
    }    
}

InnerChildren = {
   inchild1: {...},
   inchild2: {...}
}

Is there a way that I can make nested populates work when I initially load Obj1?

Error: "This operation is not supported in the environment this application is running on"

Hi - getting this error when running on a Mac. Not sure if I am doing something incorrect (fairly new to this) - using the latest 1.2 version of the library.

Exact error message in the Simulator is
This operation is not supported in the environment this application is running on. "location.protocol" must be http or https and web store must be enabled.

My store code looks like the following which is what causes the error to trigger (haven't even written any of the component code yet).

export default function configureStore(onCompletion:()=>void):any {
const enhancer = compose(
applyMiddleware(thunk.withExtraArgument(getFirebase), promise),
reactReduxFirebase(firebaseConfig, {userProfile: 'users', enableLogging: false}),
devTools({ name: 'groceries', realtime: true})
);

const store = createStore(reducer, enhancer);
persistStore(store, { storage: AsyncStorage }, onCompletion);

return store;
}

Code: https://github.com/banerjed/MobileStarter

thanks

TypeError: provider.addScope is not a function in `createAuthProvider`

I am trying to trigger a login action with the following code:

export function onTwitterLogin () {
  return (dispatch, getState, getFirebase) => {
    getFirebase().login({
      provider: 'twitter'
    })
  }
}

And I get the following error:

Uncaught (in promise) TypeError: provider.addScope is not a function
    at createAuthProvider ...

This is the stack trace:

createAuthProvider	@	auth.js:37
getLoginMethodAndParams	@	auth.js:77
login	@	auth.js:225
login	@	compose.js:147
(anonymous function)	@	auth.js:7

Looks like the addScope function doesn't exist for the TwitterAuthProvider

Populate with `childParam` does not work

Hello again :) childParam feature does not work for me. Maybe I'm doing something wrong?

Data:

goals: {
  goalId1: {
    uid: 'userId'
  }
}
users: {
  userId: {
    displayName: 'John Smith'
  }
}

Code:

const populates = [
  { child: 'uid', root: 'users', childParam: 'displayName' }
]
@firebaseConnect(() => ([
  { path: 'goals', populates }
]))
@connect(({ firebase }) => ({
  allGoals: helpers.populatedDataToJS(firebase, 'goals', populates)
}))

Result:

goals: {
  goalId1: {
    uid: {
      displayName: 'John Smith'
    }
  }
}

Expected:

goals: {
  goalId1: {
    uid: 'John Smith'
  }
}

mapState is not a function

Hey,
I have been getting this error while running this library. Is this particularly due to integrating react-redux with react-redux-firebase or some other error.

I'm using: "react-redux": "^4.0.0", "react-redux-firebase": "^1.2.0-beta"

[1] [piping] can't execute file: C:\Users\gemini\Desktop\react-universal\bin\server.js
[1] [piping] error given was: TypeError: mapState is not a function
[1] at Connect.configureFinalMapState (C:\Users\gemini\Desktop\react-universal\node_modules\react-redux\lib\components\connect.js:155:27)
[1] at Connect.computeStateProps (C:\Users\gemini\Desktop\react-universal\node_modules\react-redux\lib\components\connect.js:142:23)
[1] at Connect.updateStatePropsIfNeeded (C:\Users\gemini\Desktop\react-universal\node_modules\react-redux\lib\components\connect.js:204:35)
[1] at Connect.render (C:\Users\gemini\Desktop\react-universal\node_modules\react-redux\lib\components\connect.js:340:40)
[1] at C:\Users\gemini\Desktop\react-universal\node_modules\react-dom\lib\ReactCompositeComponent.js:796:21
[1] at measureLifeCyclePerf (C:\Users\gemini\Desktop\react-universal\node_modules\react-dom\lib\ReactCompositeComponent.js:75:12)
[1] at null.ReactCompositeComponent._renderValidatedComponentWithoutOwnerOrContext (C:\Users\gemini\Desktop\react-universal\node_modules\react-dom\lib\React
CompositeComponent.js:795:25)
[1] at null.ReactCompositeComponent._renderValidatedComponent (C:\Users\gemini\Desktop\react-universal\node_modules\react-dom\lib\ReactCompositeComponent.js:822:32)
[1] at null.ReactCompositeComponent.performInitialMount (C:\Users\gemini\Desktop\react-universal\node_modules\react-dom\lib\ReactCompositeComponent.js:362:3
0)
[1] at null.ReactCompositeComponent.mountComponent (C:\Users\gemini\Desktop\react-universal\node_modules\react-dom\lib\ReactCompositeComponent.js:258:21)

Please suggest how to resolve it.

Thanks

SET action is called multiple times

I am trying your library as away to facilitate the bindings, but I am currently hitting a dead end.
The Set Action is being constantly called, is this the expected behaviour?

this is what I am using to connect the library and redux.


const fbWrappedComponent = firebaseConnect(
    ({ profile }) => ([
        `/messages/${profile ? profile.uid : 'noUser'}/notifications`
    ])
)(UserLayout)



const authWrappedComponent = connect(
    ({ firebase }) => ({
        profile: pathToJS(firebase, 'profile')
    })
)(fbWrappedComponent)


export default connect(
       
    ({ firebase }) => ({
        authError: pathToJS(firebase, 'authError'),
        auth: pathToJS(firebase, 'auth'),
        profile: pathToJS(firebase, 'profile'),
        messagesNotifications: dataToJS(firebase, `/messages/${firebase.profile ? firebase.profile.uid : 'noUser'}/notifications`)
    },
        (dispatch) => ({
            pushRoute(r) { dispatch(push(r)) }
        })
    )
)(authWrappedComponent);`


```

Did I messed up somewhere? The way I see it the Set Action should be only called when the data changes right? 
There are no changes happening at all currrently.

Auth not populated on 2nd authorize

When I authorise user that has already been logged into the database, like so:

this.props.firebase.login({
  email: [email protected]',
  password: 'trees'
})

...the first time it happens, this.props.auth is populated with the user info, and the rest of the props (e.g. todos) are received from firebase as I would hope and subsequently render to the page.

Then, when I call firebase.logOut(), this.props.auth returns null again, and the rest of the props disappear. Again, this is the desired result.

It's when I call login() a second time that the problem occurs. The auth object is populated again like last time, but the rest of the props such as todo are not received. I tried setting the "rules" object in the console to the 'PUBLIC' setting to rule out any permissions restrictions being part of the problem but I got the same response.

The only difference in how redux receives the state from firebase between auth and todos is auth uses pathToJS, and todos uses dataToJS - could this be part of the problem?

Does the order of firebaseConnect matter?

I want to avoid directly calling this.props.firebase.push(...), so I am wrapping it in mapStateToProps, like this:

const NotesContainer = connect((state, {firebase}) => ({
  notes: dataToJS(state.firebase, '/notes'),
  createNote: (note) => firebase.push('/notes', note)
}))(Notes);

const wrappedNotes = firebaseConnect([
  '/notes'
])(NotesContainer);

export default wrappedNotes;

in examples firebaseConnect is always before redux connect. Am I safe with the approach above?

Delayed initialization

I would like to initialize firebase after user logs in in our app. In the old version of our app we use custom authentication and authenticated user can fetch firebase config from our api (apiKey, databaseUrl, authDomain) so we don't have to hardcode it in the frontend app.

Is there a way how to achieve this? I checked the source code and it looks that fbConfig is required in reactReduxFirebase and I didn't find a way how to set config values dynamically.

How to get data by user id?

Hi!

My database store data like this

/root
    /sheets
        /$user_id
            /some_data

I try to get data from Firebase by current user id. With this solution my app make tons of requests to Firebase.

const wrappedComponent = firebase((params,auth) => {
  let user = auth.auth().currentUser
  return ['/sheets/' + (user!=null?user.uid:"")]
} )(Container)

export default connect(
  ({firebase}) => ({
    sheets: dataToJS(firebase, 'sheets'),
    account: pathToJS(firebase, 'profile')
  })
)(wrappedComponent)

I think I do something wrong :) Can you help me?

ssr

the same problem as tiberiuc/redux-react-firebase#58

export const createStoreWithFirebase = compose(
    reactReduxFirebase(config),
)(createStore);

when trying to call createStoreWithFirebase on the server I get this error.
I can recreate the error if I import firebase from the server

import * as firebase from "firebase"
console.log(firebase);

Error

ReferenceError: self is not defined
    at eval (eval at ./node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1), <anonymous>:25:367)
    at eval (eval at ./node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1), <anonymous>:37:614)
    at eval (eval at ./node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1), <anonymous>:38:4)
    at Object../node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1)
    at __webpack_require__ (/srv/eat/www/server.bundle.js:21:30)
    at eval (eval at ./node_modules/firebase/firebase-browser.js (/srv/eat/www/server.bundle.js:1160:1), <anonymous>:12:1)
    at Object../node_modules/firebase/firebase-browser.js (/srv/eat/www/server.bundle.js:1160:1)
    at __webpack_require__ (/srv/eat/www/server.bundle.js:21:30)
    at eval (eval at ./node_modules/redux-react-firebase/build/compose.js (/srv/eat/www/server.bundle.js:4962:1), <anonymous>:7:17)
    at Object../node_modules/redux-react-firebase/build/compose.js (/srv/eat/www/server.bundle.js:4962:1)
ReferenceError: self is not defined
    at eval (eval at ./node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1), <anonymous>:25:367)
    at eval (eval at ./node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1), <anonymous>:37:614)
    at eval (eval at ./node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1), <anonymous>:38:4)
    at Object../node_modules/firebase/messaging.js (/srv/eat/www/server.bundle.js:1167:1)
    at __webpack_require__ (/srv/eat/www/server.bundle.js:21:30)
    at eval (eval at ./node_modules/firebase/firebase-browser.js (/srv/eat/www/server.bundle.js:1160:1), <anonymous>:12:1)
    at Object../node_modules/firebase/firebase-browser.js (/srv/eat/www/server.bundle.js:1160:1)
    at __webpack_require__ (/srv/eat/www/server.bundle.js:21:30)
    at eval (eval at ./node_modules/redux-react-firebase/build/compose.js (/srv/eat/www/server.bundle.js:4962:1), <anonymous>:7:17)
    at Object../node_modules/redux-react-firebase/build/compose.js (/srv/eat/www/server.bundle.js:4962:1)

at eval (eval at ./node_modules/redux-react-firebase/build/compose.js(/srv/eat/www/server.bundle.js:4962:1), :7:17)
line 7 on compose.js requires firebase and it errors when ran on the server

I.hasOwnProperty("process")

just import reactReduxFirebase and firebaseStateReducer, set them and:
undefined is not a function (evaluating 'I.hasOwnProperty("process")')

Example/Docs on how to implement firebase.storage

There is a new helper that should allow implementation with firebase storage. I can't quite figure out how to use it to leverage existing API's, do you have a small example or docs somewhere that I can follow?

Subscribe to path with param

Hi! The component thta firebaseConnect is returning is receiving via props an id
Then, it must subscribe to 'items/${ownProps.id}'
How can I achieve this? since the paths you pass to firebaseConnect are an array and not obtained with a function

getState() with thunk returns firebase as a map

Hey there – I'm trying to use the firebase.profile from redux in order to construct some queries to firebase, based on the user's uid. I'm having trouble accomplishing this in my action. Here's what I'm running:

export const foo = () => (dispatch, getState, getFirebase) => {
  const firebase = getFirebase()
  const state = getState()

  console.log(firebase, state)
  ...
}

And state in this case returns:
screen shot 2017-02-05 at 6 51 26 pm

Which means I can't access my profile stored in firebase.profile.

It's odd because in my redux store I can see all of this information:
screen shot 2017-02-05 at 6 54 13 pm

Does this look familiar at all?

why can you login with provider but not create user with provider?

Hi,

When I use a provider (in this case google) I would like to createUser automatically using the details available in the auth.

However createUser requires email / password.

If your using the federated oath method why the need for password?

couldn't we simply use email / uid ?

If I manually create a user with key matching auth.uid logging in successfully finds the nominated profile.

If I then go and remove permission to the app in my google account, then login and give permission again the generated uuid remains the same, i.e. the old record for the previous permission still exists in the firebase auth.

So wouldn't it be possible to also create user with: { provider: 'google', uid: 'mLHxB1FGmSg5bIkSB5PnmyWlceo2'} as credentials when using federated oath?

Id like to be able to automatically create a profile via createUser when a user logs in via a provider if no profile for that users uid exists yet.

I don't quite see the point of supporting provider based login if the user still has to do an app specific email / password signup to be able to use the profile feature.

Granted I have only used the google provider but I assume at the firebase auth db level the records are all the same

feat(populate): populated items update when changed in database

Original data:

goals: {
  goalId1: {
    uid: 'userId',
    partner: 'userId2'
  }
}
users: {
  userId: {
    displayName: 'John Smith'
  },
  userId2: {
    displayName: 'Jim Rogers'
  }
}

Data after population:

goals: {
  goalId1: {
    uid: {
      displayName: 'John Smith'
    },
    partner: {
      displayName: 'Jim Rogers'
    }
  }
}

I have noticed that after updating the original users.userId.displayName to Batman, the populated item did not update, i.e. goals.goalId1.uid.displayName remained John Smith. I think this is a good default behaviour, but it took me sometime to figure this one out. Maybe this should be mentioned somewhere in the docs?

Populate Profile Parameters

Hi,
I am currently playing around with your lib. Thanks for the effort, it looks great.

I've got one question about populate which I wasn't able to achieve. Consider the following structure:

{
  "todos": {
    "abcdef": {
      "done": false,
      "text": "example todo"
    }
  },

  "user": {
    "todos": {
      "abcdef": true
    }
  }
}

Can I use populate to query the user todos and populate them with the referenced todos?

Can't import modules

ESLint is saying:

reduxReactFirebase not found in 'react-redux-firebase'

My import statement is:

import { reduxReactFirebase } from 'react-redux-firebase';

Accessing profile '/account' errors

I am trying to access the profile menu in the example w/ material given. But I am not getting the right menu to access it. I get this on the home page in errors:
Warning: Failed propType: Required prop message was not specified in Snackbar. Check the render method of Home. warning.js?:45:9
Warning: Failed propType: Required prop message was not specified in SnackbarBody. Check the render method of WithWidth.

Even when trying to access '/account' directly I get full errors.
connect.js:81 Uncaught TypeError: Cannot read property 'firebase' of undefined
at FirebaseConnect.componentWillMount (connect.js:81)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:210)
at ReactCompositeComponentWrapper.wrapper [as mountComponent] (ReactPerf.js:66)
at Object.mountComponent (ReactReconciler.js:37)
at ReactDOMComponent.mountChildren (ReactMultiChild.js:241)
at ReactDOMComponent._createContentMarkup (ReactDOMComponent.js:591)
at ReactDOMComponent.mountComponent (ReactDOMComponent.js:479)
at Object.mountComponent (ReactReconciler.js:37)
at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:225)
at ReactCompositeComponentWrapper.wrapper [as mountComponent] (ReactPerf.js:66)

I only changed the config values for firebase for the example. Is there something else I need to do on firebase or the src?

feat(firebase): multiple firebase apps

Is it possible to connect with multiple firebase apps?
My current use case is to connect to one central firebase app that has all the keys of other firebase apps. Once the user has logged in to this app, it retrieves the key for the corresponding app and connects to it

Advanced populating?

A question regarding populate with the following data structure:

actions: {
  actionId: {
    goalId: 'goalId1',
    done: false
  },
  actionId2: {
    goalId: 'goalId1',
    done: false
  }
}
goals: {
  goalId1: {
    text: 'Goal title'
  }
}

How would I go about populating goals with actions?

Conditions:

  • User has many goals
  • Goals have many actions
  • I want to display several goals with their actions

Multiple populates do not work

Hello again :)

Having multiple fields populated does not work for me. I'm not sure if it's supposed to work.

Data:

goals: {
  goalId1: {
    uid: 'userId',
    partner: 'userId2'
  }
}
users: {
  userId: {
    displayName: 'John Smith'
  },
  userId2: {
    displayName: 'Jim Rogers'
  }
}

Code:

const populates = [
  { child: 'uid', root: 'users'},
  { child: 'partner', root: 'users' }
]
@firebaseConnect(() => ([
  { path: 'goals', populates }
]))
@connect(({ firebase }) => ({
  allGoals: helpers.populatedDataToJS(firebase, 'goals', populates)
}))

Result:

goals: {
  goalId1: {
    uid: 'userId',
    partner: 'userId2'
  }
}

Expected:

goals: {
  goalId1: {
    uid: {
      displayName: 'John Smith'
    },
    partner: {
      displayName: 'Jim Rogers'
    }
  }
}

orderByChild and equalTo breaks for an unknown reason

Hello again :)

The below example works for me (it returns goals where uid is my id), but it does not work for other users - for others it returns { "my_id": null } where my_id is 8ias7814o24j...

@connect((state) => ({
  goals: helpers.dataToJS(state.firebase, 'goals'),
}))
@firebaseConnect((props) => ([
  `/goals#orderByChild=uid&equalTo=${ props.uid }`,
]))

However, this hand made query returns the list of objects as expected.

this.props.firebase.ref('goals').orderByChild('uid').equalTo(this.props.uid).once('value').then(snapshot => {
      console.log('Here');
      console.log(snapshot.val());
    }),

So I suspect that somewhere must be a bug. Appreciate your help ;)

feat(integration): geoFire.js integration

Hi, My app use extensive amounts of geoQuery in geofire.js

I have been wondering what would be a good way to use geoFire with this module so that the result syncs with the redux state.

Please suggest!

Thanks

Expose unsupported methods

Hi!

Is there a way to expose firebase methods which are not supported by the framework? For example, I would like to use the method sendPasswordResetEmail. If yes, what would be the best way to achieve it?

Thanks in advance

Cannot read property 'ref' of undefined

I have configured my entire environment correctly, as I can see, but I got this error:

TypeError: Cannot read property 'ref' of undefined
   at FirebaseConnect.componentWillMount (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-redux-firebase/lib/connect.js:87:29)
   at /home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:348:23
   at measureLifeCyclePerf (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:75:12)
   at ReactCompositeComponentWrapper.performInitialMount (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:347:9)
   at ReactCompositeComponentWrapper.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:258:21)
   at Object.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactReconciler.js:46:35)
   at ReactCompositeComponentWrapper.performInitialMount (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:371:34)
   at ReactCompositeComponentWrapper.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:258:21)
   at Object.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactReconciler.js:46:35)
   at ReactCompositeComponentWrapper.performInitialMount (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:371:34)
   at ReactCompositeComponentWrapper.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:258:21)
   at Object.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactReconciler.js:46:35)
   at ReactCompositeComponentWrapper.performInitialMount (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:371:34)
   at ReactCompositeComponentWrapper.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:258:21)
   at Object.mountComponent (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactReconciler.js:46:35)
   at ReactCompositeComponentWrapper.performInitialMount (/home/andrey/Projects/Datarisk/app-frontend/node_modules/react-dom/lib/ReactCompositeComponent.js:371:34)

Follow my configuration:

configureStore.js

const config = {
    apiKey: '[...]',
    authDomain: '[...]',
    databaseURL: '[...]',
    storageBucket: '[...]',
};

// eslint-disable-next-line no-underscore-dangle
const composeEnhancers = (window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) || compose;
const store = createStore(reducers, persistedState, composeEnhancers(
    applyMiddleware(thunk),
    reactReduxFirebase(config),
));

reducers.js

import { combineReducers } from 'redux';
import { firebaseStateReducer } from 'react-redux-firebase';
import users from './users';

export default combineReducers({
  users,
  firebase: firebaseStateReducer,
});

AdminUserListPage.js

import { firebaseConnect, helpers } from 'react-redux-firebase';
import UserAppContainer from './UserAppContainer';
import AdminUserList from './AdminUserList';

const { dataToJS } = helpers;

const mapStateToProps = ({ firebase }) => ({
  users: dataToJS(firebase, '/Users'),
});

@firebaseConnect({ path: '/Users' })
@connect(mapStateToProps)
export default class AdminUserListPage extends Component { // eslint-disable-line
  render() {
    return (
      <UserAppContainer admin>
        <AdminUserList {...this.props} />
      </UserAppContainer>
    );
  }
}

AdminUserList.js

import { helpers } from 'react-redux-firebase';

const { isLoaded, isEmpty } = helpers;

type Props = {
  users: Object,
};

export default class AdminUserList extends Component {
  props: Props

  render() {
    const { users } = this.props;
    return (
      <div data-root id="admin-user-list">
        <h1>Usuários</h1>
        {!isLoaded(users) && <p className="loading">Carregando usuários...</p>}
        {isEmpty(users) && <p className="empty">Não há usuários cadastrados.</p>}
        {users.map(user => { ... })}
      </div>
    );
  }
}

AdminUserListPage is the wrapper of AdminUserList. I omitted some imports for brevity.

Any idea what is this about?

Filters do not seem to work with boolean values or 0

Hello,

It seems that ordering by boolean values does not work.

@firebaseConnect([
  '/goals#orderByChild=done&equalTo=true'
])

It does not work with 0 as well

@firebaseConnect([
  '/goals#orderByChild=done&equalTo=0'
])

It works fine with other values. Any ideas why this might be the case?

Once does not work

It is being bound like any other event instead of a special once case.

imports failing inside reducer and store

I am trying to use as the boilerplate https://github.com/wellyshen/react-cool-starter but on running build:production I get a weird export warning and error with no imports working. Weirdly though other packages seem to be working just fine
WARNING in ./src/redux/store.js
26:83-101 "export 'reactReduxFirebase' was not found in 'react-redux-firebase'
at HarmonyImportSpecifierDependency.getWarnings (C:\Users\gemini\Desktop\rea
ct-cool-starter-master\node_modules\webpack\lib\dependencies\HarmonyImportSpecif
ierDependency.js:46:15)
at Compilation.reportDependencyErrorsAndWarnings (C:\Users\gemini\Desktop\re
act-cool-starter-master\node_modules\webpack\lib\Compilation.js:663:24)
at Compilation.finish (C:\Users\gemini\Desktop\react-cool-starter-master\nod
e_modules\webpack\lib\Compilation.js:526:9)
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\webpack\li
b\Compiler.js:472:16
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\tapable\li
b\Tapable.js:225:11
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\webpack\li
b\Compilation.js:472:11
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\webpack\li
b\Compilation.js:443:13
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickCallback (node.js:349:13)

WARNING in ./src/redux/reducers.js
31:14-34 "export 'firebaseStateReducer' was not found in 'react-redux-firebase'
at HarmonyImportSpecifierDependency.getWarnings (C:\Users\gemini\Desktop\rea
ct-cool-starter-master\node_modules\webpack\lib\dependencies\HarmonyImportSpecif
ierDependency.js:46:15)
at Compilation.reportDependencyErrorsAndWarnings (C:\Users\gemini\Desktop\re
act-cool-starter-master\node_modules\webpack\lib\Compilation.js:663:24)
at Compilation.finish (C:\Users\gemini\Desktop\react-cool-starter-master\nod
e_modules\webpack\lib\Compilation.js:526:9)
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\webpack\li
b\Compiler.js:472:16
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\tapable\li
b\Tapable.js:225:11
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\webpack\li
b\Compilation.js:472:11
at C:\Users\gemini\Desktop\react-cool-starter-master\node_modules\webpack\li
b\Compilation.js:443:13
at nextTickCallbackWith0Args (node.js:420:9)
at process._tickCallback (node.js:349:13)

ERROR in .//react-redux-firebase/src/connect.js
Module parse failed: C:\Users\gemini\Desktop\react-cool-starter-master\node_modu
les\react-redux-firebase\src\connect.js Unexpected token (43:24)
You may need an appropriate loader to handle this file type.
| }
|
| static contextTypes = {
| store: PropTypes.object.isRequired
| };
@ ./
/react-redux-firebase/src/index.js 1:0-31
@ ./src/redux/store.js
@ ./src/client.js

ERROR in .//react-redux-firebase/src/helpers.js
Module parse failed: C:\Users\gemini\Desktop\react-cool-starter-master\node_modu
les\react-redux-firebase\src\helpers.js Unexpected token (242:10)
You may need an appropriate loader to handle this file type.
| if (dataToJS(data, path)[p.child]) {
| return {
| ...dataToJS(data, path),
| [p.child]: buildChildList(data, dataToJS(data, path)[p.child], p)
| }
@ ./
/react-redux-firebase/src/index.js 5:0-36
@ ./src/redux/store.js
@ ./src/client.js

ERROR in .//react-redux-firebase/src/actions/auth.js
Module parse failed: C:\Users\gemini\Desktop\react-cool-starter-master\node_modu
les\react-redux-firebase\src\actions\auth.js Unexpected token (253:12)
You may need an appropriate loader to handle this file type.
| firebase,
| { uid },
| { ...extraJWTData, uid }
| )
| }
@ ./
/react-redux-firebase/src/actions/index.js 1:0-37
@ .//react-redux-firebase/src/compose.js
@ ./
/react-redux-firebase/src/index.js
@ ./src/redux/store.js
@ ./src/client.js

ERROR in main.5e6876c477fe44fd6224.js from UglifyJs
SyntaxError: Unexpected character '`' [main.5e6876c477fe44fd6224.js:17271,9]

Please let me know what should be done

Thanks

May need appropriate loader

ERROR in ./~/react-redux-firebase/src/connect.js
Module parse failed: <project-directory>\node_modules\react-redux-firebase\src\connect.js Unexpected token (43:24)
You may need an appropriate loader to handle this file type.
|     }
|
|     static contextTypes = {
|       store: PropTypes.object.isRequired
|     };
 @ ./~/react-redux-firebase/src/index.js 1:0-31
 @ ./ui/src/index.js

ERROR in ./~/react-redux-firebase/src/helpers.js
Module parse failed: <project-directory>\node_modules\react-redux-firebase\src\helpers.js Unexpected token (252:14)
You may need an appropriate loader to handle this file type.
|           if (dataToJS(data, pathString)) {
|             return {
|               ...dataToJS(data, path),
|               [p.child]: dataToJS(data, pathString)
|             }
 @ ./~/react-redux-firebase/src/index.js 5:0-36
 @ ./ui/src/index.js

ERROR in ./~/react-redux-firebase/src/actions/auth.js
Module parse failed: <project-directory>\node_modules\react-redux-firebase\src\actions\auth.js Unexpected token (283:12)
You may need an appropriate loader to handle this file type.
|           firebase,
|           { uid },
|           { ...extraJWTData, uid }
|         )
|       }
 @ ./~/react-redux-firebase/src/actions/index.js 1:0-37
 @ ./~/react-redux-firebase/src/compose.js
 @ ./~/react-redux-firebase/src/index.js
 @ ./ui/src/index.js`

I got this error after adding react-redux-router to my project.

webpack: v2.2.1
webpack.config.js

module.exports = {
	entry: './ui/src/index.js',
	output: {
		path: path.resolve(__dirname, 'ui/dist'),
		filename: 'bundle.js',
	},
	resolve: {
		alias: {
			actions: path.resolve(__dirname, 'ui/src/actions'),
			App: path.resolve(__dirname, 'ui/src/App'),
			assets: path.resolve(__dirname, 'ui/src/assets'),
			components: path.resolve(__dirname, 'ui/src/components'),
			reducers: path.resolve(__dirname, 'ui/src/reducers'),
			root: path.resolve(__dirname, 'ui/src'),
			utils: path.resolve(__dirname, 'ui/src/utils'),
		},
	},
	target: 'electron',
	devtool: 'eval-source-map',
	module: {
		rules: [
			{
				test: /\.js$/,
				exclude: /node_modules/,
				use: 'babel-loader',
			},
			{
				test: /\.css$/,
				use: ExtractTextPlugin.extract({
					fallback: 'style-loader',
					use: 'css-loader'
				})
			},
			{
				test: /\.less$/,
				use: ExtractTextPlugin.extract({
					fallback: 'style-loader',
					use: 'css-loader!less-loader'
				})
			},
			{
				test: /\.(jpg|png|svg)$/,
				use: 'url-loader',
			},
		],
	},
	plugins: [
		new ExtractTextPlugin('styles.css'),
	],
};

.babelrc

{
  "presets": [
    "stage-0",
    "node5",
    "react"
  ],
  "plugins": [
    "transform-react-display-name",
    "transform-decorators",
    "transform-class-properties",
    "transform-es2015-classes"
  ]
}

Dealing with route changes

The problem I have right now is that on route change, the data lags with updates.

For example, at one page I show goals of a specific user. On another page I show goals of all users. When the route chagnes, the goals and the state update after a small time interval (~100-500ms) and this does not look pretty.

Is there a way to maybe:

  • delete the data on component unmount?
  • do not mount the component until firebase syncs data?

Any suggestions would be highly appreciated :)

Binding { type: 'once', ... } fires Query.off which returns an error

Hi there!

I get an error after binding the data once, like this:

@firebaseConnect((props) => ([
  { type: 'once', path: DH.getUserProfile(props.uid) },
  { type: 'once', path: DH.getUserGoalsPath(props.uid) },
]))

Below is the error I get:

database.js:130 Uncaught Error: Query.off failed: first argument must be a valid event type: "value", "child_added", "child_removed", "child_changed", or "child_moved".
    at jf (http://localhost:8080/index.js:37664:156)
    at U.g.Ic (http://localhost:8080/index.js:37707:59)
    at unsetWatcher (http://localhost:8080/index.js:36456:46)
    at unWatchEvent (http://localhost:8080/index.js:34494:35)
    at http://localhost:8080/index.js:34516:13
    at Array.forEach (native)
    at unWatchEvents (http://localhost:8080/index.js:34515:18)
    at FirebaseConnect.componentWillUnmount (http://localhost:8080/index.js:31687:37)
    at http://localhost:8080/index.js:23652:26
    at measureLifeCyclePerf (http://localhost:8080/index.js:23318:13)

I figure this is because 'once' does not have any watchers which should be unset. However, couldn't find a fix in the code. Would appreciate your help on this one!

bug(helpers): dataToJS retrieves a new copy each render

Using dataToJS() in the @connect() function breaks the shouldComponentUpdate functionality -- that is to say, every time @connect() calls dataToJS(), it gets a new object returned even if the database contents are the same.

This is an issue as it causes unnecessary re-rendering of the component.

Is this a known issue? Is there a workaround for it?

Key Within Populated Data

Is there a way to leave the ID in the populated object as opposed to it being replaced by the data?

This is the data:

goals: {
  text: 'Something'
  user: 'userId'
}
users: {
  userId: { online: true }
}

This is the outcome of populate:

goals: {
  text: 'Something',
  user: { online: true }
}

Is there a way to make the outcome as follows:

goals: {
  text: 'Something',
  user: { user: 'userId', online: true }
}

or

goals: {
  text: 'Something',
  user: 'userId',
  userData: { online: true }
}

The first option would be to include the key used in the populated object. The second option would be to specify the target child for populate values.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.