I'm trying to figure out how to use react-firebase-hooks in my react app so that I can simplify calls on my database.
My previous version (solved with help on this question) of this attempt used this class component with a componentDidMount function (it worked):
class Form extends React.Component {
state = {
options: [],
}
async componentDidMount() {
// const fsDB = firebase.firestore(); // Don't worry about this line if it comes from your config.
let options = [];
await fsDB.collection("abs_for_codes").get().then(function (querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, ' => ', doc.data());
options.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id
});
});
});
this.setState({
options
});
}
I'm now trying to learn how to use hooks to get the data from the database using react-firebase-hooks. My current attempt is:
import { useDocumentOnce } from 'react-firebase-hooks/firestore';
I have also tried import { useDocument } from 'react-firebase-hooks/firestore';
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes'),
options.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id
}),
);
This generates an error that says: 'useDocumentOnce' is not defined
I tried (that's also incorrect):
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes'),
{snapshot.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id,
})},
);
How do I get a collection from firebase? I'm trying to populate a select menu with the options read from a collection in firebase called abs_for_codes.
I think the point of useState is that I don't need to declare a state anymore, I can just call I have added my select attempt below:
<Select
className="reactSelect"
name="field"
placeholder="Select at least one"
value={valuesSnapshot.selectedOption}
options={snapshot}
onChange={handleMultiChangeSnapshot}
isMulti
ref={register}
/>
For reference, I have 2 other select menus in my form. The consts I use to set up the options for those are manually defined, but the process to establish their values is below:
const GeneralTest = props => {
const { register, handleSubmit, setValue, errors, reset } = useForm();
const { action } = useStateMachine(updateAction);
const onSubit = data => {
action(data);
props.history.push("./ProposalMethod");
};
const [valuesStudyType, setStudyType] = useState({
selectedOptionStudyType: []
});
const [valuesFundingBody, setFundingBody] = useState({
selectedOptionFundingBody: []
});
const handleMultiChangeStudyType = selectedOption => {
setValue("studyType", selectedOption);
setStudyType({ selectedOption });
};
const handleMultiChangeFundingBody = selectedOption => {
setValue("fundingBody", selectedOption);
setFundingBody({ selectedOption });
};
useEffect(() => {
register({ name: "studyType" });
register({name: "fundingBody"});
}, []);
How do I add the snapshot from the database query?
I have tried making similar handleMultiChange const and useEffect register statements for the snapshot, like so:
const [snapshot, loading, error] = useDocumentOnce(
firebase.firestore().collection('abs_for_codes'),
snapshot.push({
value: snapshot.data().title.replace(/( )/g, ''),
label: snapshot.data().title + ' - ABS ' + snapshot.id
}),
);
const [valuesField, setField ] = useState({
selectedOptionField: []
});
const handleMultiChangeField = selectedOption => {
setValue("field", selectedOption);
setField({ selectedOption });
};
but it doesn't work. The error message says:
ReferenceError: Cannot access 'snapshot' before initialization
I can't find an example of how to populate the select menu with the data from the database.
NEXT ATTEMPT
useEffect(
() => {
const unsubscribe = firebase
.firestore()
.collection('abs_for_codes')
.onSnapshot(
snapshot => {
const fields = []
snapshot.forEach(doc => {
fields.push({
value: fields.data().title.replace(/( )/g, ''),
label: fields.data().title + ' - ABS ' + fields.id
})
})
setLoading(false)
setFields(fields)
},
err => {
setError(err)
}
)
return () => unsubscribe()
})
This doesn't work either - it produces an error message that says:
TypeError: fields.data is not a function
NEXT ATTEMPT
Recognising that i need to search the collection rather than a call on document, but still not sure whether useCollectionData is more appropriate than useCollectionOnce (I can't make sense of the documentation about what useCollectionData offers), I have now tried:
const [value, loading, error] = useCollectionOnce(
firebase.firestore().collection('abs_for_codes'),
{getOptions({
firebase.firestore.getOptions:
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id,
})},
);
This is also incorrect. The error message points to the getOptions line and says: Parsing error: Unexpected token, expected ","
In my collection, i have a number of documents. Each has 2 attributes, a number and a text string. My options are to format the number and the text string so they appear together as well as an acronym i have inserted as text (as I was able to do using componentDidMount).
NEXT ATTEMPT
I next tried this:
const fields = firebase.firestore.collection("abs_for_codes").get().then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
console.log(doc.id, ' => ', doc.data());
fields.push({
value: doc.data().title.replace(/( )/g, ''),
label: doc.data().title + ' - ABS ' + doc.id
});
});
});
The error message says: TypeError: _firebase__WEBPACK_IMPORTED_MODULE_5__.firebase.firestore.collection is not a function
NEXT ATTEPMT
const searchFieldOfResearchesOptions = (searchKey, resolver) => {
// for more info
// https://stackoverflow.com/questions/38618953/how-to-do-a-simple-search-in-string-in-firebase-database
// https://firebase.google.com/docs/database/rest/retrieve-data#range-queries
fsDB
.collection("abs_for_codes")
.orderBy("title")
// search by key
.startAt(searchKey)
.endAt(searchKey + "\uf8ff")
.onSnapshot(({ docs }) => {
// map data to react-select
resolver(
docs.map(doc => {
const { title } = doc.data();
return {
// value: doc.id,
// label: title
value: title.data().title.replace(/( )/g, ''),
label: title.data().title + ' - ABS ' + title.id
};
})
);
}, setFieldOfResearchesError);
};
This attempt actually works to retrieve data from the database (hooray) - except I can't get the text label I want to render. Each document in the collection has 2 fields. The first is a title and the second is an id number, my last step is to make a label that has inserted text (ie ABS - ) and then the id number and the title together.
I have added the commented code to show what works to extract each document's title, but the extra bit I tried to make the label the way I want it doesn't present an error, it just doesn't work - I still only get the document title in the list.
Does anyone know how to generate a select menu set of options from a cloud firestore collection using hooks?