Closed zoomclub closed 6 years ago
No plans at the moment, although I'd gladly comment on any PRs you send my way. We've been discussing how to get Firebase working with React Native and I think this also ties into that a little bit. Do you have any concrete ideas here?
Great to learn about the discussion on Firebase and RN integration! In terms of component development I'm just looking for the optimal way for Firebase to provide the data and then for editing that data on a component, in light of the rapid progress being made with ES6 and React.
There are a number of proposals out there (the ones I mentioned) and I'm considering them in the context of the app I'm engineering. I have an app with more than just a couple gears to it so it provides the dynamic to work with, I'll be in touch with what I resolve.
Hi, any development on how to use mixins: [ReactFireMixin],
in ES6? for a react project not react native. thanks.
There are still no active plans to update ReactFire for ES6 / React Native. You may want to check out re-base (https://github.com/tylermcginnis/re-base) which may fulfill your needs.
+1
+1
+1
+1
React is phasing out mix-ins with the change to ES6 classes.
The React team chose not to allow the existing mix-in pattern when creating a React component the ES6 way because JS doesn't currently have a standard method for doing mix-ins. So React.createClass
and existing mix-in usage for components probably won't go anywhere until JS has an approved spec for mix-ins. Why not take the wait and see approach rather than trying to blaze that trail?
The nice thing is you can mix both ES6 class and createClass
approaches for creating React components in your apps, and simply prefer one over the other until the path forward becomes evident.
There are new frameworks like Cycle.js than need a Firebase driver too. The driver is their specific way of adding a service to Cycle.js.
IDEA! Instead of Firebase always making a specific package per given framework why not create a core dropin service and then a set of plugins for specific things like Relay, Falcor, etc. Actually, Relay and Falcor are less robust copycats of Firebase any ways and the core aspects of Firebase should remain the main focus.
Surely, it is not the best to always have to completely customize per framework. Would it not be better for Firebase to instead provide a ES6 module with the essential core features available as higher order functions and then provide plugins for the rest?
This could always be done using the existing Firebase API but there is still a lot of re-inventing the wheel per project that way. To me Firebase stands on its own as a global service to XYZ framework components and the bridge into the specific component architecture of a given framework should be as thin as possible.
It is to be expected that any savvy framework can work with ES6 modules and a library of higher order functions. While a framework may come and go Firebase does not!
Not a bad idea @HighOnDrive. I know you'd really like to see a Cycle.js driver, but we just don't have the resources right now to investigate and develop one. We'd love to see something come out of the community though!
As for your idea about creating an ES6 module with core features, I think that is compelling. For example, it certainly would be nice to not have to recreate the realtime array logic for every new binding. However, with our core product, we need to be more cautious for many reasons: a lot more people use it, a lot more people abuse it, and we support a lot of legacy browsers. With things like ReactFire, we can be more aggressive, dropping support for old browsers and providing APIs which people may (unintentionally) abuse. It's a tough line to tread and in order for us to move it into the core SDK, we need to be very sure of it.
I appreciate the discussion here and I hope it continues. I'd love to hear more about your ideas here!
resources can be solved by hiring :-)
Parse maintains a similar mixin for connecting to React, but also provides a method for using ES6 classes for use with observables a la RxJS and the related React proposal. Is that something which seems worthwhile? Regardless users can still leverage REST and Flux, and leverage the new Firebase CLI tools for Node while building.
I had a little go at designing a fresh firebase API as a higher order component. This is what I came up with.
const ref = new FirebaseEndpoint('https://myapp.firebaseio.com');
@connectFirebase((props) => {
return { // this allows us to reconfigure when out targets props change
// if needed this could also prolly return a promise
endpoint: ref, // should allow passing this via this.context too
keys: {
user: '/users/$userid',
messages: `/rooms/${props.roomId}/messages`,
},
paths: {
'/users/$userid': null,
`/rooms/${props.roomId}/messages`: {
isArray: true,
query: {
limitToLast: 10,
},
},
},
};
}, (keys, states, actions) => {
return { // this object becomes assigned to the wrapped components props
// we can also require more complicated actions and state reducers here and mix them in in more complex apps
user: states[keys.user],
messages: states[keys.messages],
updateUser: ({name}) => actions[keys.user].set({name}), // firebase will automatically trigger a change even right away and keep the props update
};
})
class App extends Component {
render() {
const {updateUser, user} = this.props;
return (
<div>
Hi <input value={user.firstname} onChange={(evt) => updateUser(evt.target.value)} type="text"/>
</div>
);
}
}
We need a RxJS module for Firebase that is not tied into any specific framework. The same module could then work with all the frameworks that have already moved to RxJS. When is Firebase going to quit sitting on their asses about this and get with the program!?
I mean it's a new day now with FRP in the picture, just check out what happened at the Reactive 2015 conference or the RxJS videos being presented from Angular2 events, etc, etc. etc, etc.
Cycle.js also made a real dent in the universe at Reactive 2015, here is an article in case anyone wants a jump start on the future: https://medium.com/@fkrautwald/plug-and-play-all-your-observable-streams-with-cycle-js-e543fc287872
:+1: GraphQL & RxJS would be awesome!
It can help to mixin into react es6 component https://github.com/brigand/react-mixin
import React, { Component } from 'react'
import Firebase from 'firebase'
import ReactFireMixin from 'reactfire'
import reactMixin from 'react-mixin'
const ref = new Firebase('https://<APPNAME>.firebaseio.com/users')
class UsersList extends Component {
constructor (props, context) {
super(props, context)
this.state = {
users: []
}
}
componentDidMount () {
this.bindAsArray(ref, 'users')
}
renderUser (user) {
return <p>{user.name}</p>
}
render () {
return (
<div>
{this.state.languages.map(this.renderLanguage)}
</div>
)
}
}
reactMixin(UsersList.prototype, ReactFireMixin)
export default UsersList
I created gist. You can fork it here: https://gist.github.com/kulakowka/24bb83775358ad4c3bc7
ES6 is dead. Long live COBOL!
Jacob's answer continues to be the latest and greatest. Posts adding value on how devs can get up to speed on ES 6 or working toward a PR are welcome. Posts asking for release dates or discussing other features will be pruned. Please keep discussion on topic and productive.
Hello
Here is a proposal for a PR. Provide a single method à la redux "connect" that provides the current mixin methods to the component class. EDIT: The terminology seems to be a High Order Component Usage would look like:
import { Component } from 'react';
import reactFire from 'reactfire';
class MyComponent extends Component {
}
export default reactFire(MyComponent);
//OR ES7 decorator style
@reactFire()
export default class MyComponent extends Component {
}
So here is a first shot at it https://gist.github.com/fdidron/bde084f08ce64025ffba4f57c4eb441a
There is an API breaking change, since it's using a high order component, everything happens in your component's props.
So you access ReactFire methods via props : this.props.bindAsArray
, ...
And the binded data can be found in the props of your component instead of the state.
@fdidron I love the decorator idea. With many devs using Babel this could be a nice syntactic gain that still achieves the same idea as the mixin.
I'd like to propose a createFirebaseContainer
function (or possibly just createContainer
, which would work as follows:
createFirebaseContainer(component, refs);
FirebaseRefs
(see below) object or function that returns such an objectIf refs
is a function, it should take in the props that were passed to the container when rendered, and returns a FirebaseRefs
object.
FirebaseRefs
objectA FirebaseRefs
object, is an object that looks like this:
{
"<propName>": {
"ref": DatabaseRef,
"type": "array" | "object",
},
...
}
Here, "<propname>"
is the name of a property you want to receive.
ref
should be the database reference you want to bind that property to.
type
should be either "array"
or "object"
depending on whether you want to bind to that reference as an array or an object.
Here's an example for a TodoList
component:
const TodoList = React.createClass({
render(){
return <ul>
{this.props.todos.map((todo) => {
return <li><input type="checkbox" defaultChecked={todo.done}> {todo.text}</li>
})}
</ul>
}
});
export default createFirebaseContainer(TodoList, {
todos: {
ref: firebase.database().ref("/todos"),
type: "array"
}
});
Now, when you render the exported component, it would receive a todos
prop, which represents the value in the database at /todos
. When the value changes, the component is automatically re-rendered.
refs
You may not know in advance where the data you need lives. For example, a TodoList will likely only want to show todos for a specific user. These items could live at /todos/{userID}
. To make it easier to specify the refs at runtime, you can pass a function like this:
const TodoList = function(props) {
return <ul>
{props.todos.map((todo) => {
return <li><input type="checkbox" defaultChecked={todo.done}> {todo.text}</li>
})}
</ul>
};
export default createFirebaseContainer(TodoList, (props) => {
return {
todos: {
ref: firebase.database().ref("/todos/" + props.userID),
type: "array"
}
}
});
Now, assuming we import the exported component as TodoList
, we can render it as <TodoList userID="123abc" />
, and the todos for that user would be fetched.
This function could be returned as an attribute of ReactFireMixin
. This way, the API is backwards compatible. The wrapper component could also make use of ReactFireMixin
internally, for a long as React supports mixins using createClass
.
Thanks @fdidron and @bigblind for the proposals. I like both of them! @fdidron's is nice since it keeps practically the same API but just allows you to use it via props
instead of via methods on the component itself. @bigblind is a bigger breaking change as it rethinks how you specify what nodes you are binding to.
One question I have about @bigblind's proposal is how you handle unbinding and re-binding the same reference. For example, if you have an app which displays the last ten items and then you want to now display only five, how would you handle that? I'd imagine the wrapped component's state
would contain the limit value. But then the outer createFirebaseContainer()
wrapper HOC won't have access to that value. So now you have to be using something like Redux to store that state in a place that the HOC can access it.
I also think we need to consider the fact that Firebase is now much more than just a Realtime Database. It also has Authentication (for signing in and managing users) and Storage (for storing files and blobs). I want to make sure the new version of ReactFire isn't so Database-centric.
I think I still need to think on these proposals a bit more and prototype some things with both. If anyone else out there has thoughts or opinions or other proposals, please join this thread.
A while ago I forked reactfire (pre-3.0) to allow for bind to be called multiple times on the same component: https://github.com/marcello3d/reactfire/compare/master...marcello3d:rebind
This made it really easy to do the following:
componentWillMount () {
this._bind(this.props)
},
componentWillReceiveProps (nextProps) {
this._bind(nextProps)
},
_bind ({ id }) {
this.bindAsObject(root.child('users').child(id), 'user')
}
It made my logic a lot more declarative and easier to reason about.
It's implemented by unbinding and rebinding when binding to a new ref.
(The fork also treats bindAsArray as null
instead of []
so you can distinguish between "not loaded yet" and an empty array—though the implementation is crude.)
@jwngr The container that is returned by createContainer has componentWillMount and componentWillUnmount methods to handle binding/unbinding. What we can do, is apart from the props specified by the caller, also pass in a firebase
object, which has methods for unbinding.
With regards to how the specific refs can change, for things like changing the number of objects returned, or changing the sort order, if you pass a function as the refs
parameter, it will be re-called anytime the props change. So, fo let's say I have the following code:
const TodoList = function(props) {
return <ul>
{props.todos.map((todo) => {
return <li><input type="checkbox" defaultChecked={todo.done}> {todo.text}</li>
})}
</ul>
};
export default createFirebaseContainer(TodoList, (props) => {
return {
todos: {
ref: firebase.database().ref("/todos/" + props.userID).limitToFirst(props.maxTodos),
type: "array"
}
}
});
if you import this component as UserTodos
, you can render it as: <UserTodos userID="abc", maxTodos={10} />
.
If you need to store the limit, or other query data somewhere in component state, you can wrap the container in another component that keeps this data in state, and passes it as properties to the container.
Regarding auth, you could add a .auth
path, which is caught by the client, and instead of binding to it in the database, it binds to the auth event listener. Or, you can have an FirebaseAuthContainer
, which passes auth data as props to its children.
Here is another idea :)
I think it would be nice to have reactfire provide a JSX component allowing Firebase binding. NOTE: I'm not saying this should be the only way to bind !
const eventTypes = [ 'value', 'child_added', 'child_changed', 'child_removed', 'child_moved'];
FireAct.defaultProps = {
eventType: 'value'
}
FireAct.propTypes = {
bindTo: PropTypes.any,
children: PropTypes.element,
endAt: PropTypes.any,
equalTo: PropTypes.any,
eventType: PropTypes.oneOf(eventTypes).isRequired,
limitToFirst: PropTypes.number,
limitToLast: PropTypes.number,
once: PropTypes.bool,
onDataChange: PropTypes.func,
orderByChild: PropTypes.string,
orderByKey: PropTypes.bool,
orderByValue: PropTypes.bool,
orderByPriority: PropTypes.bool,
push: PropTypes.bool,
startAt: PropTypes.any,
url: PropTypes.string.isRequired
}
Listen for data changes at a particular location.
import React from 'react';
import FireAct from 'fireact';
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: null
}
}
dataChangeHandler(data) {
//Code to handle data change (update the state for example).
}
render() {
return (
<FireAct
onDataChange={this.dataChangeHandler.bind(this)}
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
);
}
}
By default, FireAct listens to value
events. You can use the evenType Prop to listens to other events ([ 'value', 'child_added', 'child_changed', 'child_removed', 'child_moved']
)
Listens for exactly one event of the specified event type, and then stops listening
<FireAct
once
onDataChange={this.dataChangeHandler.bind(this)}
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Generates a new Query object ordered by the specified child key.
<FireAct
onDataChange={this.dataChangeHandler.bind(this)}
orderByChild="height"
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Generates a new Query object ordered by key.
<FireAct
onDataChange={this.dataChangeHandler.bind(this)}
orderByKey
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Generates a new Query object ordered by key.
<FireAct
onDataChange={this.dataChangeHandler.bind(this)}
orderByValue
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Generates a new Query object ordered by key.
<FireAct
onDataChange={this.dataChangeHandler.bind(this)}
orderByPriority
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Creates a Query with the specified starting point. The generated Query includes children which match the specified starting point.
<FireAct
onDataChange={this.dataChangeHandler.bind(this)}
orderByChild="height"
startAt={3}
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Creates a Query with the specified ending point. The generated Query includes children which match the specified ending point.
<FireAct
endat="pterodactyl"
onDataChange={this.dataChangeHandler.bind(this)}
orderByKey
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Creates a Query which includes children which match the specified value.
<FireAct
equalTo={25}
onDataChange={this.dataChangeHandler.bind(this)}
orderByChild="height"
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Generates a new Query object limited to the first certain number of children.
<FireAct
limitToFirst={2}
onDataChange={this.dataChangeHandler.bind(this)}
orderByChild="height"
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
Generates a new Query object limited to the last certain number of children.
<FireAct
limitToLast={2}
onDataChange={this.dataChangeHandler.bind(this)}
orderByChild="weight"
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
In order to write data use the bindTo
prop. Any time the data passed to bindTo changes, it will be updated on firebase.
render(){
let data = {
user: 'Homer',
message: 'I love Pork Chops'
}
return (
<FireAct
bindTo={data}
url="https://fireact.firebaseio.com/someId"
/>
);
}
If you want to push the data in a new child location with a unique key, add the push prop [➤]
render(){
let data = {
user: 'Homer',
message: 'I love Pork Chops'
}
return (
<FireAct
bindTo={data}
push
url="https://fireact.firebaseio.com/dinosaurs"
/>
);
}
You can achieve two way binding by using both the bindTo and the onChangeData props :
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
data: {
user: 'Homer',
message: 'I love Pork Chops'
}
}
}
dataChangeHandler(data) {
this.setState({
data: data
});
}
render() {
return (
<FireAct
bindTo={this.state.data}
onDataChange={this.dataChangeHandler.bind(this)}
url="https://dinosaur-facts.firebaseio.com/dinosaurs"
/>
);
}
}
I like @fdidron's idea of using a react component for the FB values. Still some work to be done on more complex examples, but it seems like a reasonably direction.
There are 2 things that somewhat bother me about @fdidron's proposal.
First of all, you're adding properties for every single option and operation for Firebase refs. Why not pass in the ref as a single property? This decouples the library more from Firebase, allowing it to keep working, even when Firebase expands its functionality.
But what I find weird as well is the way data flows. Usually, a component gets the data it needs from its parent. The only situation where components usually get data from a child using a callback, is when that child needs to be rendered by the component, and the component needs to respond to user input. What would the FireAct
component render? If nothing at all, then, why is it in the render tree?
@bigblind Concerning your first comment, I understand the tradeoff of coupling the Firebase lib to the component. However the idea is to provide a declarative JSX way of binding to Firebase, a bit like react router allows you to declare routes.
For the second comment, I agree with you, come to think of It, I could make the component a container (or Provider like redux) and pass down the binded value as props to its direct children. I could even pass the value further down nested components using react's context, and provide a function to map the bindings to props. What do you think ?
Saw this library, might be worth looking at as well: https://github.com/tiberiuc/redux-react-firebase
Great discussion.
Its nice having decorators or connecting to state management of some kind (like redux) so your smart components can more easily specify their data dependencies.
redux-react-firebase uses decorators which is nice, but it isn't updated to Firebase v3+ yet.
I think A component is way overcomplicating the API. It should just be a single call to attach methods to the class or instance, or just static methods on the ReactFire
object that take a this
property:
class User extends React.Component {
constructor (props) {
fPosition = props.fPlayer.child("position")
fContacts = props.fPlayer.child("contacts")
ReactFire.bindAsObject(this, "position", fPosition)
ReactFire.bindAsArray(this, "contacts", fContacts)
}
render () {
{contacts, position} = state
{x, y} = position
<div class="user" style={left: x, top: y}>
<Contacts contacts={contacts}/>
</div>
}
}
(Those methods could do some initialization the first time something is binded, and overload the componentWillUnmount
, removing listeners and calling the original. Perhaps not the cleanest solution internally, but it would make for a nice succinct API.)
@1j01 That requires any object that wants to use Firebase to be a class, so you can't use it with function components. I still think a container is the most flexible option. Anything where you still need to manage binding/unbinding requires keeping state, which I think you should avoid as much as possible.
@bigblind Okay, well I didn't consider function components (I've never used them) Has ReactFire previously worked with function components? (And does it really need to?)
I just spent 3 hours writing an elaborate answer to this question only then to accidentally open another page in that tab... 😭
Here's the gist of it:
I might update this post with more useful links when I'm less depressed about browsers not saving draft form responses... 3 hours... wasted.
@bigblind What browser are you using? Chrome seems pretty good about saving things, navigating forwards, backwards, closing and reopening tabs. Not that I haven't lost plenty of stuff to Chrome.
Anyways, seems like a reasonable approach.
Hey, what's the current status of this conversation?
I actually finally found some time this past weekend to test out a new HOC API myself. I am hoping to have a proposal in PR form soon. I'm leaning towards going with an API similar to what @bigblind suggested, but there are still a few performance concerns that need to be figured out (mainly around re-binding Firebase listeners upon changes to props).
@jwngr That is awesome to hear! Excited to check it out.
@jwngr Sweet, I will be eagerly waiting! @prescottprue love your v3 lib on firebase redux! That's what's keeping me a float currently.
Thanks @birdwell, that is exactly what it is for!
For those who haven't seen it: redux-firebasev3 was made as a way to get v3 implemented in React/Redux. I figure that ReactFire will take its place eventually, but I needed a solution before then.
Same with re-base, there's a PR awaiting merge which adds v3 support: https://github.com/tylermcginnis/re-base/pull/104
@oscar-b re-base is already updated to v3
Here's a completely different approach I've been experimenting with: https://gist.github.com/marcello3d/0e9a1e532954f67c1fbb67c7d18319a6
I'm bypassing ReactFire altogether, and instead made two APIs:
Example usage:
import React from 'react'
import { observeValue, observeCurrentUser } from './firebase-observable'
import connectObserver from './connect-observer'
export default connectObserver({
// function takes props and returns an Observable or Promise
post: ({ postId }) => observeValue(firebase.database().ref('/posts').child(postId)),
user: () => observeCurrentUser(firebase.auth())
})(class Post extends React.Component
static propTypes = {
postId: React.PropTypes.string.isRequired,
post: React.PropTypes.shape({ … }),
user: React.PropTypes.object
};
render () {
const { post, user } = this.props
return …
}
}))
Do the owners of the reactfire repo have a recommended approach, or stated plans for using reactfire w/out mixins?
@hyperbrave - https://github.com/firebase/reactfire/issues/38#issuecomment-238967140 is still the latest. I really want to get this out but I haven't found the time to finish my proposal yet.
Hi! I've been working on a library of my own for a while, and finally I managed to get it to a stage that it's ready for sharing. (altough there still may be bugs) I'm starting to use it for a project of my own. The main idea is to implement the Flux architecture with Google Firebase as the store and using Rx for the unidirectional flow. Although it is completely independent of React it works quite well with it. It also features a local in-memory storage with similar one-way data binding to variables or callbacks. Check out the library at https://kozta.github.io/fluxbase Any ideas, comments, questions are welcome.
React is phasing out mixins with the change to ES6 classes. What is the plan for the reactfire mixin with this coming change? Could reactfire also tie in with react contexts? Below is a link to the context approach:
https://blog.jscrambler.com/react-js-communication-between-components-with-contexts
React relay with graphql is another approach coming to react. It would seem like reactfire updated to work with ES6 classes and the context approach might be a powerful alternative to relay with added collaboration! It might also work to incorporate some of the graphql as well?