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.
 
    