Brian Paskoff
Brian Paskoff

Reputation: 101

React + Firebase using hooks?

I found a great tutorial on using React with Firebase for a very simple use of just storing a text field's data in a database, but I'm stumped on how to convert it to using Hooks. There aren't many tutorials I could find out there on using Hooks + Firebase, so I'd appreciate any help!

import React, {useState} from 'react'
import fire from './fire'
import './App.css'

const App = () => {
  let [messageList, setMessageList] = useState([])

  const handleSubmit = (e) => {
    e.preventDefault()
  }

  return(
    <section>
    <form onSubmit={handleSubmit}>
      <input type="text"></input>
      <button>Test</button>
    </form>
    <span>Messages: {messageList}</span>
    </section>
  )
}

class ThisWorksButNotWithHooks extends React.Component {
  constructor(props) {
    super(props);
    this.state = { messages: [] }; // <- set up react state
  }
  componentWillMount(){
    /* Create reference to messages in Firebase Database */
    let messagesRef = fire.database().ref('messages').orderByKey().limitToLast(100);
    messagesRef.on('child_added', snapshot => {
      /* Update React state when message is added at Firebase Database */
      let message = { text: snapshot.val(), id: snapshot.key };
      this.setState({ messages: [message].concat(this.state.messages) });
    })
  }
  addMessage(e){
    e.preventDefault(); // <- prevent form submit from reloading the page
    /* Send the message to Firebase */
    fire.database().ref('messages').push( this.inputEl.value );
    this.inputEl.value = ''; // <- clear the input
  }
  render() {
    return (
      <form onSubmit={this.addMessage.bind(this)}>
        <input type="text" ref={ el => this.inputEl = el }/>
        <input type="submit"/>
        <ul>
          { /* Render the list of messages */
            this.state.messages.map( message => <li key={message.id}>{message.text}</li> )
          }
        </ul>
      </form>
    );
  }
}

export default App;

I know it's communicating with Firebase properly because ThisWorksButNotWithHooks works, thanks to https://www.codementor.io/yurio/all-you-need-is-react-firebase-4v7g9p4kf . I learn best by seeing a barebones simple example and working up from there by trial and error, so if I knew how to do this with Hooks I could work up to more complex usage.

Thank you!

Upvotes: 2

Views: 5295

Answers (3)

Jeff
Jeff

Reputation: 2490

The react-firebase-hooks library provides convenience methods for Hooks + Firebase. You can take a look at their useList implementation to get some ideas.

Upvotes: 0

Chris Riesbeck
Chris Riesbeck

Reputation: 131

According to the Firebase Realtime API page, the useEffect() body should end with

return () => messagesRef.off('value', listener)

because on() returns the listener, not the unlistener. Firestore's onDataSnapshot() is the one that returns the unlistener.

Upvotes: 2

M.Lewis
M.Lewis

Reputation: 1041

Something like this should work. I haven't really focused on your Firebase logic or returned html, but just showed how you could use the useEffect and useState hooks:

const App = () => {
  const [messageList, setMessageList] = useState([])
  const [inputEl, setInputEl] = React.useState(null)
  
  React.useEffect(() => {
    /* Create reference to messages in Firebase Database */
    let messagesRef = fire.database().ref('messages').orderByKey().limitToLast(100);

    // As you are using a listener, declare it so it can be returned
    const listener = messagesRef.on('child_added', snapshot => {

      // Below logic just adds to the current 'messages' state  
      const message = { text: snapshot.val(), id: snapshot.key };
      const updatedMessagesArray = [...messageList].push(message)
      setMessageList(updatedMessagesArray)
    })

    return () => listener() // <== the listener returns the unsubscribe function, which the hook will automatically run when the component unmounts.

  }, []) // <== run once onMount

  async function addMessage(e){
    e.preventDefault(); // <- prevent form submit from reloading the page
    /* Send the message to Firebase */
    await fire.database().ref('messages').push( inputEl.value );
    setInputEl(null)
  }
  
  const handleSubmit = (e) => {
    e.preventDefault()
  }

 return(
    <section>
    <form onSubmit={handleSubmit}>
      <input type="text"></input>
      <button>Test</button>
    </form>
    <span>Messages: {messageList}</span>
    </section>
  )
  
}

Passing an empty array as the second arg to the useEffect hook will tell it only to run once when the component mounts, and returning the listener will let the hook unsubscribe the listener when the component unmounts.

Upvotes: 4

Related Questions