Skip to content

Commit

Permalink
v2.0.0-beta.18 (#345)
Browse files Browse the repository at this point in the history
* feat(populate): `childAlias` for store results of populate on another parameter - #126
* feat(profile): Firestore support for `updateProfile` method - [issue 25 on redux-firestore](prescottprue/redux-firestore#25)
* feat(storage): `progress` option added to `uploadFile` method - #346
* feat(storage): `uploadFile` default metadata written to DB now includes `createdAt`
* feat(core): `redux-firestore` is no longer a dependency - managed by library user directly
* fix(examples): Material-ui example updates (including moving `injectTapEventPlugin()` to `main.js`)
  • Loading branch information
prescottprue committed Dec 3, 2017
1 parent 4578a37 commit f70e74e
Show file tree
Hide file tree
Showing 29 changed files with 1,608 additions and 1,486 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ sudo: false
language: node_js

node_js:
- 6.11.3 # Still Used
- 6.11.5 # Cloud Functions Runtime (Used for firebase-functions)
- 8 # L.T.S
- 9 # Current

Expand Down
78 changes: 48 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,49 @@ export default compose(
)(Todos)
```

**Queries Based On Props**

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

```jsx
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { firebaseConnect, getVal } from 'react-redux-firebase'

// Component enhancer that loads todo into redux then into the todo prop
const enhance = compose(
firebaseConnect((props) => [
// Set listeners based on props (prop is route parameter from react-router in this case)
return [
{ path: `todos/${props.params.todoId}` }, // create todo listener
// `todos/${props.params.todoId}` // equivalent string notation
]
}),
connect(({ firebase }, props) => ({
todo: getVal(firebase, `todos/${props.params.todoId}`), // lodash's get can also be used
}))
)

const Todo = ({ todo, firebase, params }) =>
<div>
<input
name="isDone"
type="checkbox"
checked={todo.isDone}
onChange={() =>
firebase.update(`todos/${params.todoId}`, { done: !todo.isDone })
}
/>
<span>{todo.label}</span>
</div>

// Export enhanced component
export default enhance(Todo)
```


**Load Data On Click**

```jsx
Expand Down Expand Up @@ -188,41 +231,16 @@ const Todos = ({ firebase }) => {
)
}

// Export enhanced component
export default compose(
withFirebase, // or firebaseConnect()
connect(
(state) => ({
todos: state.firebase.data.todos,
// profile: state.firebase.profile // load profile
})
)
connect((state) => ({
todos: state.firebase.data.todos,
// profile: state.firebase.profile // load profile
}))
)(Todos)
```

**Queries Based On State**
`Todos` component from above examples

```jsx
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { compose } from 'redux'
import { firebaseConnect } from 'react-redux-firebase'

export default compose(
firebaseConnect((props, store) => {
const state = store.getState();
// Get Todos stored by user UID
return state.auth ? [`todos/${state.auth.uid}`] : []
}),
connect(
(state) => ({
todos: state.firebase.data.todos,
// profile: state.firebase.profile // load profile
})
)
)(Todos)
```

## [Docs](http://react-redux-firebase.com)
See full documentation at [react-redux-firebase.com](http://react-redux-firebase.com)
Expand Down
148 changes: 49 additions & 99 deletions docs/getting_started.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,38 +11,6 @@ npm install --save react-redux-firebase

Install peer dependencies: `npm i --save redux react-redux`

### Decorators

Though they are optional, it is highly recommended that you used decorators with this library. [The Simple Example](examples/simple) shows implementation without decorators, while [the Decorators Example](examples/decorators) shows the same application with decorators implemented.

A side by side comparison using [react-redux](https://github.com/reactjs/react-redux)'s `connect` function/HOC is the best way to illustrate the difference:

```jsx
class SomeComponent extends Component {

}
export default connect()(SomeComponent)
```
vs.

```jsx
@connect()
export default class SomeComponent extends Component {

}
```

In order to enable this functionality, you will most likely need to install a plugin (depending on your build setup). For Webpack and Babel, you will need to make sure you have installed and enabled [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy) by doing the following:

1. run `npm i --save-dev babel-plugin-transform-decorators-legacy`
2. Add the following line to your `.babelrc`:
```json
{
"plugins": ["transform-decorators-legacy"]
}
```


## Install
```bash
npm install --save react-redux-firebase
Expand All @@ -55,15 +23,15 @@ Include `firebase` in your combine reducers function:

```js
import { combineReducers } from 'redux'
import { firebaseStateReducer } from 'react-redux-firebase'
import { firebaseReducer } from 'react-redux-firebase'

// Add firebase to reducers
const rootReducer = combineReducers({
firebase: firebaseStateReducer
firebase: firebaseReducer
})
```

## Compose Function
## Setting Up Store With Store Enhancer

```js
import { compose } from 'redux'
Expand All @@ -84,7 +52,7 @@ const config = {

// Add redux Firebase to compose
const createStoreWithFirebase = compose(
reactReduxFirebase(firebaseConfig, config)
reactReduxFirebase(firebase, config)
)(createStore)

// Create store with reducers and initial state
Expand All @@ -95,76 +63,58 @@ View the [config section](/config.html) for full list of configuration options.

## Use in Components

**Queries Based On State**
`Todos` component from above examples

```jsx
import React, { Component } from 'react'
import React from 'react'
import PropTypes from 'prop-types'
import { connect } from 'react-redux'
import { firebaseConnect, isLoaded, isEmpty } from 'react-redux-firebase'

@firebaseConnect([
'todos' // corresponds to 'todos' root on firebase
])
@connect(
({ firebase: { data: { todos } } }) => ({ // state.firebase.data.todos
// todos prop set to firebase data in redux under '/todos'
todos,
})
)
export default class Todos extends Component {
static propTypes = {
todos: PropTypes.object,
firebase: PropTypes.object
}

handleAdd = () => {
const {newTodo} = this.refs
const { firebase } = this.props
// Add a new todo to firebase
firebase.push('/todos', { text: newTodo.value, done: false })
newTodo.value = ''
}

render() {
const { todos } = this.props;

// Build Todos list if todos exist and are loaded
const todosList = !isLoaded(todos)
? 'Loading'
: isEmpty(todos)
? 'Todo list is empty'
: Object.keys(todos).map(
(key, id) => (
<TodoItem key={key} id={id} todo={todos[key]}/>
)
)

return (
<div>
<h1>Todos</h1>
<ul>
{todosList}
</ul>
<input type="text" ref="newTodo" />
<button onClick={this.handleAdd}>
Add
</button>
</div>
)
}
import { compose } from 'redux'
import { firebaseConnect } from 'react-redux-firebase'

export default compose(
firebaseConnect((props) => {
return [
'todos'
]
}),
connect(
(state) => ({
todos: state.firebase.data.todos,
// profile: state.firebase.profile // load profile
})
)
)(Todos)
```

### Decorators

They are completely optional, but ES7 Decorators can be used. [The Simple Example](examples/simple) shows implementation without decorators, while [the Decorators Example](examples/decorators) shows the same application with decorators implemented.

A side by side comparison using [react-redux](https://github.com/reactjs/react-redux)'s `connect` function/HOC is the best way to illustrate the difference:

```jsx
class SomeComponent extends Component {

}
export default connect()(SomeComponent)
```
vs.

Alternatively, if you choose not to use decorators, your connect function will look like so:
```jsx
@connect()
export default class SomeComponent extends Component {

```javascript
const wrappedTodos = firebaseConnect([
'todos'
])(Todos)
}
```

export default connect(
({ firebase: { data: { todos } } }) => ({
todos,
})
)(wrappedTodos)
In order to enable this functionality, you will most likely need to install a plugin (depending on your build setup). For Webpack and Babel, you will need to make sure you have installed and enabled [babel-plugin-transform-decorators-legacy](https://github.com/loganfsmyth/babel-plugin-transform-decorators-legacy) by doing the following:

1. run `npm i --save-dev babel-plugin-transform-decorators-legacy`
2. Add the following line to your `.babelrc`:
```json
{
"plugins": ["transform-decorators-legacy"]
}
```
46 changes: 43 additions & 3 deletions docs/populate.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export default enhance(SomeComponent)
* Population happens in two parts:
1. `firebaseConnect` - based on populate settings, queries are created for associated keys to be replaced. Query results are stored in redux under the value of `root` in the populate settings.
2. `connect` - Combine original data at path with all populate data (in redux from queries created by passing populate settings to `firebaseConnect`)
* Populate creates a query for each key that is being replaced
* Populate creates a query for each key that is being "populated", but does not create multiple queries for the same key
* Results of populate queries are placed under their root

## Examples
Expand Down Expand Up @@ -135,10 +135,13 @@ ASDF123: {
}
```

##### Keep Object's Key
##### Keep Object's Key {#keyProp}

Often when populating, you will want to keep the key that was originally there (before being replaced by populated value). This is supported through the use of `keyProp`:


*NOTE:* Similar results also be accomplished using [`childAlias`](#childAlias) since child (key in this case) will be preserved if populate result is "aliased"

##### Example Query
```javascript
const populates = [
Expand Down Expand Up @@ -170,7 +173,44 @@ ASDF123: {
}
```

### Object's Parameter
### Place Result On Another Parameter {#childAlias}

There is also the option to place the results of a populate on another parameter instead of replacing the original child (i.e. "alias" the child result). An example of this could be populating the `owner` parameter onto the `ownerObj` parameter, which would leave the `owner` parameter intact (since the child from the populate was "aliased" to `ownerObj`).

For more details including the initial feature request, checkout [issue #126](https://github.com/prescottprue/react-redux-firebase/issues/126).

##### Example
```javascript
const populates = [
{ child: 'owner', root: 'users', childAlias: 'ownerObj' }
]

const enhance = compose(
firebaseConnect([
{ path: '/todos', populates }
// '/todos#populate=owner:users:email' // equivalent string notation
]),
connect(
({ firebase }) => ({
todos: populate(firebase, 'todos', populates),
})
)
)
```

##### Example Result

```javascript
ASDF123: {
text: 'Some Todo Item',
owner: "Iq5b0qK2NtgggT6U3bU6iZRGyma2",
ownerObj: {
email: 'mortysmith@gmail.com',
}
}
```

### Object's Parameter {#childParam}

There is also the option to load a parameter from within a population object. An example of this could be populating the `owner` parameter with the `email` property of the `user` with a matching ID:

Expand Down
Loading

0 comments on commit f70e74e

Please sign in to comment.