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.