Dan Zuzevich
Dan Zuzevich

Reputation: 3831

React Array Map With Firebase Issue

So I am running into a slight issue in regards to this basic task app I am creating with React, Firebase, and Node. I have the working version hosted on heroku(will post link below) so you can use it to see what I mean.

My Issue

If you visit the link to my app, you can see that when you initially click the get tasks button, the tasks do not get loaded to the screen. It takes two clicks to get the list to populate to the screen.

Also, when you click the close icon to try to remove the specific task, well... you will see with your own two eyes what happens, lol.

My Thoughts Although I am happy that it atleast works, I feel as if the main culprit is the code I am creating inside of the grabTasks function. Don't ask why I created a random empty array with the variable name x, I have no clue either, it was the only way I could get it to work. I've read all the firebase docs, so I am hoping someone here can enlighten me on what the issue is, and maybe suggest a better way to go about this. Code & Links below.

Link to working Heroku App

App.js

// App.js
export default class App extends Component {
  constructor(props) {
    super(props);

    this.state = { tasks: [] };
    this.grabTasks = this.grabTasks.bind(this);
    this.handleDelete = this.handleDelete.bind(this);
  }

  // Grabs the list of tasks from the firebase database
  // and then sets the state to x.
  grabTasks() {
    var x = [];
    db.ref('tasks').on('value', function(snapshot) {
       return x = snapshot.val();
    });
    this.setState({ tasks : x });
  }

  // In charge of removing the task items when they are clicked.
  // This function is passed to TaskItems via props
  handleDelete(taskToDelete) {
       var newTasks = _.reject(this.state.tasks, function(task) {
           return task == taskToDelete
       });
       this.setState({tasks: newTasks});
     }

  render() {
    return (
      <div id="wrapper">
        <div id="navigation">
          <Navbar />
        </div>
        <div id="content">
          <div className="container">
            <div className="row">
              <h2>Tasks</h2>
              <div onClick={this.grabTasks} className="btn">Get Tasks</div>
              <TaskItems tasks={this.state.tasks} handleDelete={this.handleDelete} />
            </div>
          </div>
        </div>
      </div>
    )
  }
}

TaskItems.js

// TaskItems Child Component
const TaskItems = (props) => {
  var hello = props.tasks.map((task,index) => {
    return(
      <li className="collection-item" key={index}>
        {task}
        <a onClick={props.handleDelete.bind(this, task)}>
          <i className="fa fa-times-circle remove-task" aria-hidden="true"></i>
        </a>
      </li>
    );
  });

  return (
    <div>
      <ul className="collection">
        {hello}
      </ul>
    </div>
  );
}

export default TaskItems

Upvotes: 3

Views: 2097

Answers (1)

Phi Nguyen
Phi Nguyen

Reputation: 3056

Your db.ref('tasks') is an async method. You shouldn't think it as sync. Try this:

grabTasks() {
 var that = this;
 db.ref('tasks').on('value', function(snapshot) {
   var x = snapshop.val();
   that.setState({tasks : x})
 });
}

To get those tasks when the screen is loaded :

componentDidMount(){
  this.grabTasks();
}

Upvotes: 2

Related Questions