1

I'm trying to figure out how to get data from firestore via react hooks.

For this attempt, I have tried to follow the example in this blog post .

I made a record in firestore in a collection called "impact_metrics".

I'm trying to figure out how to display it in react, using hooks.

My list is:

import React, { useState, useEffect } from 'react';
import {Link } from 'react-router-dom';
import Typography from '@material-ui/core/Typography';
import ImpactMetricsForm from "./Form";
import firebase, { firestore } from "../../../firebase.js";
import { makeStyles } from '@material-ui/core/styles';
import clsx from 'clsx';
import ExpansionPanel from '@material-ui/core/ExpansionPanel';
import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
import ExpansionPanelActions from '@material-ui/core/ExpansionPanelActions';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Chip from '@material-ui/core/Chip';
import Button from '@material-ui/core/Button';
import Divider from '@material-ui/core/Divider';

const useStyles = makeStyles((theme) => ({
  root: {
    width: '100%',
  },
  heading: {
    fontSize: theme.typography.pxToRem(15),
  },
  secondaryHeading: {
    fontSize: theme.typography.pxToRem(15),
    color: theme.palette.text.secondary,
  },
  icon: {
    verticalAlign: 'bottom',
    height: 20,
    width: 20,
  },
  details: {
    alignItems: 'center',
  },
  column: {
    flexBasis: '33.33%',
  },
  helper: {
    borderLeft: `2px solid ${theme.palette.divider}`,
    padding: theme.spacing(1, 2),
  },
  link: {
    color: theme.palette.primary.main,
    textDecoration: 'none',
    '&:hover': {
      textDecoration: 'underline',
    },
  },
}));

const Title = {
    fontFamily: "'Montserrat', sans-serif",
    fontSize: "4vw",
    marginBottom: '2vh'
};

const Subhead = {
    fontFamily: "'Montserrat', sans-serif",
    fontSize: "calc(2vw + 1vh + .5vmin)",
    marginBottom: '2vh',
    marginTop: '8vh',
    width: "100%"
};

function useImpactMetrics() {
    const [impactMetrics, setImpactMetrics] = useState([])
    useEffect(() => {
      firebase
        .firestore()
        .collection("impact_metrics")
        .onSnapshot(snapshot => {
          const impactMetrics = snapshot.docs.map(doc => ({
            id: doc.id,
            ...doc.data(),
          }))
          setImpactMetrics(impactMetrics)
        })
    }, [])
    return impactMetrics
  }

const ImpactMetricsMenu = () => {

    const impactMetrics = useImpactMetrics()
    const classes = useStyles();

    return ( 
        <div style={{ marginLeft: "3vw"}}>
            <Typography variant="subtitle" className="blogParagraph" style={Subhead}>Select Metrics</Typography>
            
            
                
                    
                    
                       
            
            <div className={classes.root}>
            tet
            {impactMetrics.map(impactMetric => {
                return (
                <ExpansionPanel defaultExpanded>
                <ExpansionPanelSummary
                    expandIcon={<ExpandMoreIcon />}
                    aria-controls="panel1c-content"
                    id="panel1c-header"
                >
                    <div className={classes.column}>
                    <Typography className={classes.heading}>{impactMetric.title}</Typography>
                    </div>
                    <div className={classes.column}>
                    <Typography className={classes.secondaryHeading}>Select trip destination</Typography>
                    </div>
                </ExpansionPanelSummary>
                <ExpansionPanelDetails className={classes.details}>
                    <div className={classes.column} />
                    <div className={classes.column}>
                    <Chip label="Barbados" onDelete={() => {}} />
                    </div>
                    <div className={clsx(classes.column, classes.helper)}>
                    <Typography variant="caption">
                        Select your destination of choice
                        <br />
                        <a href="#secondary-heading-and-columns" className={classes.link}>
                        Learn more
                        </a>
                    </Typography>
                    </div>
                </ExpansionPanelDetails>
                <Divider />
                <ExpansionPanelActions>
                    <Button size="small">Cancel</Button>
                    <Button size="small" color="primary">
                    Save
                    </Button>
                </ExpansionPanelActions>
                </ExpansionPanel>

            )
        })}
            </div>
            sdf
        </div>   

     );
}
 
export default ImpactMetricsMenu;

When I try this, I get an error in the console that says:

Uncaught Error in onSnapshot: FirebaseError: Missing or insufficient permissions.

My firestore rules have:

// impact_metrics
    match /impact_metrics/{impactMetricId} {
      allow read;
      allow write;
    }

What else is required to give this permission?

I wondered if it might have something to do with the timing of the call on firebase, so I tried to follow the solution in this post to make the call on firestore async, as set out below. It's incorrect, but I can't see why.

function useImpactMetrics() {
    const [impactMetrics, setImpactMetrics] = useState([])
    useEffect(() => {
      const fetchImpactMetrics = async () => {
      firebase
        .firestore()
        .collection("impact_metrics")
        .onSnapshot(snapshot => {
          const impactMetrics = await snapshot.docs.map(doc => ({
            id: doc.id,
            ...doc.data(),
          }))
          setImpactMetrics(impactMetrics)
        })
    }, [fetchImpactMetrics])
    return impactMetrics
  }

NEXT ATTEMPT

There is a note in firestore that the rules system changed late last year. I tried opting into version two and adding an explicit list permission in the rule.

I had understood from the generic starter kit that conditions were not a mandatory reqiurement for rules, but in an attempt to solve this problem, I added them.

The new rule (including adding version 2 to the top of the rules list) is:

 // impact_metrics
    match /impact_metrics/{impactMetricId} {
      allow read: if true;
      allow list: if true;
      allow write: if true;
    }

When I try this, I get the same console error.

Mel
  • 2,481
  • 26
  • 113
  • 273

1 Answers1

0

the console error is occurred by firestore security rules.

You should set the firestore security read rule to true with conditions.

At first, try the following code.

ex. allow public read access

service cloud.firestore {
  match /databases/{database}/documents {
    match /impact_metrics/{impactMetricId} {
      allow read: if true;
    }
  }
}

Secondly, you should set any conditions you want.

See:

zkohi
  • 2,486
  • 1
  • 10
  • 20
  • Thanks for the suggestion. Although the starter kit comes with dev mode rules that don't include conditions, I tried your suggestion. I get the same error. I also tried opting into v2 of the security rules and am still not getting anywhere toward resolving this problem. Thanks for trying to help. – Mel Aug 09 '20 at 02:15
  • Could you test your Firebase Security Rules in the Firebase console, use the Rules Playground? See https://firebase.google.com/docs/rules/simulator – zkohi Aug 10 '20 at 11:48