0

I am making a react/redux app that is using a firestore database for storage and I was able to link up my app to firestore so that I can create, read, update, and destroy new data to firestore from within my app. The problem is, on one of my pages when I update data it instantly updates that data in the backend firestore database but that change is not reflected on screen on the page immediately. Instead, it only shows after I do a hard refresh of the page. Also, I am currently using https://github.com/prescottprue/react-redux-firebase and https://github.com/prescottprue/redux-firestore for working with firebase in my react/redux app.

So, what do I need to do to make my page load in new changes automatically, as I thought using react-firestore's "firestoreConnect" would work but it is not. Thanks guys and here is my code:

Data Component

import React, { Component } from "react";
import { connect } from "react-redux";
import { firestoreConnect, isLoaded } from "react-redux-firebase";
import { compose } from "redux";
import { Grid } from "semantic-ui-react";
import BudgetHeader from "../BudgetHeader/BudgetHeader";
import BudgetList from "../BudgetList/BudgetList";
import BudgetNav from "../BudgetNav/BudgetNav";
import { updateBudgetData, compareChooser } from "../budgetActions";
import LoadingComponent from "../../../app/layout/LoadingComponent";

const mapState = state => ({
  users: state.firestore.ordered.users
});

const actions = {
  updateBudgetData,
  compareChooser
};

class BudgetDashboard extends Component {
  updateBudgetData = (newData, fieldToUpdate) => {
    this.props.updateBudgetData(newData, fieldToUpdate);
  };

  onCompareChange = (event, data) => {
    if (data.checked) {
      this.props.compareChooser("net");
    } else {
      this.props.compareChooser("gross");
    }
  };

  render() {
    const { users } = this.props;
    if (!isLoaded(users)) return <LoadingComponent inverted={true} />;
    return (
      <Grid>
        <Grid.Column width={16}>
          <BudgetNav />
          <BudgetHeader
            general={users[0].budget[1]}
            onCompareChange={this.onCompareChange}
          />
          <BudgetList
            className="standardSeparation"
            data={users[0].budget[0]}
            unallocatedBudget={users[0].budget[1].unallocatedBudget}
            unallocatedSpending={users[0].budget[1].unallocatedSpent}
            updateData={this.updateBudgetData}
          />
        </Grid.Column>
      </Grid>
    );
  }
}

export default compose(
  connect(
    mapState,
    actions
  ),
  firestoreConnect([
    {
      collection: "users",
      doc: "asdfasdfF21871d",
      subcollections: [{ collection: "budget" }]
    }
  ])
)(BudgetDashboard);

Component Actions (where update budget method is housed)

export const updateBudgetData = (inputData, fieldToUpdate) => {
  return async (dispatch, getState, { getFirebase, getFirestore }) => {
    try {
      const firestore = getFirestore();

      // Get all firestore data nodes that will be used for updating after data change
      const userBudget = firestore
        .collection("users")
        .doc("asdfasdfas8128fsa")
        .collection("budget");
      const userDataRef = userBudget.doc("data");
      const userGeneralRef = userBudget.doc("general");

      // variables to be used in generating updated maps
      var grossIncome = 0;
      var netIncome = 0;
      var userDataMap = {};
      var userGeneralMap = {};

      // get grid data
      await userDataRef
        .get()
        .then(function(doc) {
          if (doc.exists) {
            userDataMap = doc.data();
          } else {
            console.log("No user data");
          }
        })
        .catch(function(error) {
          console.log("User Data Document Get Error: ", error);
        });
      // get header data
      await userGeneralRef
        .get()
        .then(function(doc) {
          if (doc.exists) {
            const data = doc.data();
            userGeneralMap = data;
            grossIncome = data.grossIncome;
            netIncome = data.netIncome;
          } else {
            console.log("No general data");
          }
        })
        .catch(function(error) {
          console.log("User General Document Get Error: ", error);
        });

      // generate new data and general document mappings
      const updatedMaps = generateUpdatedMaps(
        userDataMap,
        userGeneralMap,
        grossIncome,
        netIncome,
        inputData,
        fieldToUpdate
      );
      const newUserDataMap = updatedMaps.newDataMap;
      const newUserGeneralMap = updatedMaps.newGeneralMap;

      // update firestore data with new mappings
      userDataRef.set(newUserDataMap);
      userGeneralRef.set(newUserGeneralMap);

      toastr.success("Success!", "Budget data has been updated");
    } catch (error) {
      toastr.error("Oops", "Something went wrong");
    }
  };
};

I thought by wrapping my BudgetDashboard component in firestoreConnect the listener would automatically update when changes occur but that is not happening. Instead only on a hard refresh does my LoadingComponent show and the new data finally is reflected on screen. So how do I make any change immediately reflect a change on screen?

FrankTheTank
  • 765
  • 2
  • 12
  • 30
  • not sure if it's what you want, but what I have is this: on my mapDispatchToProps i have a method called 'goToHomeScreen'(for example) and on this method I used 'ownProps' and with this i dispatch a reset to the screen. The screen will be reseted with the new props from store – kivul Nov 23 '18 at 15:54
  • Can you elaborate on this a little bit? Where would I be putting mapDispatchToProps, you mean in the redux connect? Could you post a code snippet so I have a better idea how you structured your code. Thanks a lot for the help – FrankTheTank Nov 23 '18 at 15:58
  • i'll post this as an answer, i think it's better to understand the code – kivul Nov 23 '18 at 16:21

1 Answers1

0

So, imagine that the screen you need to refresh is 'screen1'. It would go like this:

import { NavigationActions, StackActions } from 'react-navigation';

default class Screen1 extends Component {
  constructor(props) {
    super(props);
  }

  <<stuff from your component>>   
}

const mapDispatchToProps = (dispatch, ownProps) => {
  goToHomeScreen: () => { 
    ownProps.navigation.dispatch(StackActions.reset({index: 0, key: null, actions: [NavigationActions.navigate({ routeName: '**<<ROUTE YOU WANT TO GO>>**'})]}))
  },
}

export default connect(mapDispatchToProps)(Screen1);

So, for example, after you fetched your data from the store, and then you need to refresh the screen, you'd call it like:

this.props.goToHomeScreen()

I hope this helps, not sure if it's what you want, but thats a way I found to refresh the screen after getting the new props from store.

kivul
  • 1,148
  • 13
  • 28
  • Oh I see so you are saying whenever I fetch data just reload the screen on a manual call. Is there a way I could reload just say the div holding the data? – FrankTheTank Nov 23 '18 at 16:37
  • Also I will mess around with this more once I am home later today – FrankTheTank Nov 23 '18 at 16:37
  • for that i dont know, I guess there could be a way, never tried to figure it out though – kivul Nov 23 '18 at 16:43
  • also, I believe that you could use componentWillReceiveProps or the new one getDerivedStateFromProps (since component~ is deprecated). So that everytime your props change you refresh the screen.. not sure how it would work, but maybe it's something to give it a try – kivul Nov 23 '18 at 16:46