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?
Consuming Data from Firestore First, import db from the config file along with useState and useEffect to create state , and fire the request to fetch data. Create a piece of state to store your data. Create an async function to fetch data from Firestore and call it inside useEffect , as shown below.
First, you need to create your application in the Firebase console. Then, head over to the Database menu and scroll a down a bit into the Choose Real-Time Database section. Set the security rules to start in test mode. This makes your database insecure, but it's okay for the purpose of this tutorial.
Navigate to the Cloud Firestore section of the Firebase console. You'll be prompted to select an existing Firebase project. Follow the database creation workflow.
import { useDocument } from 'react-firebase-hooks/firestore';
Why ? You are using useDocumentOnce, of course you need to import this function, and no useDocument, which you don't use.
Last error:You are using a const before even it has been initialized, hence the
ReferenceError: Cannot access 'snapshot' before initialization
snapshot is going to be initialized by useDocumentOnce, you cannot use it (snapshot) as an argument passed to the function going to initialize it.
Plus, I had a look on react-firebase-hooks, here is the documentation of useDocumentOnce:
Use this example, and adapt it to use the document you want to use.
import { useDocument } from 'react-firebase-hooks/firestore';
const FirestoreDocument = () => {
const [value, loading, error] = useDocument(
firebase.firestore().doc('hooks/nBShXiRGFAhuiPfBaGpt'),
{
snapshotListenOptions: { includeMetadataChanges: true },
}
);
return (
<div>
<p>
{error && <strong>Error: {JSON.stringify(error)}</strong>}
{loading && <span>Document: Loading...</span>}
{value && <span>Document: {JSON.stringify(value.data())}</span>}
</p>
</div>
);
};
You can either use useDocument as in the example, but you can also choose to use useDocumentOnce. But in this case, change the import accordingly (to import { useDocumentOnce } from 'react-firebase-hooks/firestore';
😏
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With