Populate react select options array with key value pairs from firebase collection

强颜欢笑 提交于 2020-03-01 03:00:51

问题


I am trying to use an options array in my react app, that uses react-select for the form and where the options are stored in a firebase collection.

This all works fine when I define a const in the form with an array of options that I define with key value pairs, but I'm struggling to figure out how to replace that array with the collection stored in Firebase (Cloud Firestore).

In my form, I currently have:

const options = [
  { value: "neurosciences", label: "Neurosciences - ABS 1109" },
  { value: "oncologyCarcinogenesis", label: "Oncology and Carcinogenesis  - ABS 1112" },
  { value: "opticalPhysics", label: "Optical Physics  - ABS 0205" },
  { value: "fisheriesSciences", label: "Fisheries Sciences - ABS 0704" },
  { value: "genetics", label: "Genetics - ABS 0604" },
  { value: "urbanRegionalPlanning", label: "Urban and Regional Planning - ABS 1205" }
];

I want to replace this array, with a map over the document titles in the database collection.

The document name in my database has the key and each document has a single field called 'title'.

Thank in my form select I have:

<div className="form-group">
                            <label htmlFor="fieldOfResearch">
                            Select your field(s) of research
                            </label>

                            <Select
                            key={`my_unique_select_key__${fieldOfResearch}`}
                            name="fieldOfResearch"
                            isMulti
                            className={
                                "react-select-container" +
                                (errors.fieldOfResearch && touched.fieldOfResearch ? " is-invalid" : "")
                            }
                            classNamePrefix="react-select"
                            value={this.state.selectedValue1}
                            onChange={e => {
                                handleChange1(e);
                                this.handleSelectChange1(e);
                            }}
                            onBlur={setFieldTouched}
                            options={options}
                            />
                            {errors.fieldOfResearch && touched.fieldOfResearch && 
                            <ErrorMessage
                            name="fieldOfResearch"
                            component="div"
                            className="invalid-feedback d-block"
                            />}
                            </div>

I have read the firebase documents on using arrays, but I am missing something (probably obvious) that has led me down at least 20 different paths for how to do this.

I'm not sure if this is relevant, but my forms are built with Formik.

How do I replace the const options array with a map over key value pairs from the firebase database collection?

I have tried to define my options constant as:

const options = fsDB.collection("abs_for_codes")

but the page fills up with errors that I can't decipher. I have read this user guide, but don't understand the directions relating to indexes and I'm not even clear on whether they're what I need to know for this problem.

https://firebase.google.com/docs/firestore/query-data/queries

I have also tried:

const options = fsDB.collection("abs_for_codes").get().then(function (querySnapshot) {
    querySnapshot.forEach(function (doc))
}

but that's just guessing from trying to make sense of the documentation.

When I try the exact formulation shown in the firebase docs, as:

const options = fsDB.collection("abs_for_codes");

options.get().then(function (querySnapshot) {
    querySnapshot.forEach(function (doc) {
        console.log(doc.id, ' => ', doc.data());
    });
});

I get a full page of indecipherable error messages, as follows:

TypeError: options.reduce is not a function
Select.buildMenuOptions
node_modules/react-select/dist/react-select.esm.js:4123
  4120 |   };
  4121 | };
  4122 | 
> 4123 | return options.reduce(function (acc, item, itemIndex) {
       | ^  4124 |   if (item.options) {
  4125 |     // TODO needs a tidier implementation
  4126 |     if (!_this3.hasGroups) _this3.hasGroups = true;
View compiled
new Select
node_modules/react-select/dist/react-select.esm.js:3593
  3590 | 
  3591 | var _selectValue = cleanValue(value);
  3592 | 
> 3593 | var _menuOptions = _this.buildMenuOptions(_props, _selectValue);
       | ^  3594 | 
  3595 | _this.state.menuOptions = _menuOptions;
  3596 | _this.state.selectValue = _selectValue;
View compiled
constructClassInstance
node_modules/react-dom/cjs/react-dom.development.js:11787
  11784 |     new ctor(props, context); // eslint-disable-line no-new
  11785 |   }
  11786 | }
> 11787 | var instance = new ctor(props, context);
        | ^  11788 | var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
  11789 | adoptClassInstance(workInProgress, instance);
  11790 | {
View compiled
updateClassComponent
node_modules/react-dom/cjs/react-dom.development.js:15265
  15262 |   } // In the initial pass we might need to construct the instance.
  15263 | 
  15264 | 
> 15265 |   constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
        | ^  15266 |   mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
  15267 |   shouldUpdate = true;
  15268 | } else if (current$$1 === null) {
View compiled
beginWork
node_modules/react-dom/cjs/react-dom.development.js:16265
  16262 | 
  16263 |     var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
  16264 | 
> 16265 |     return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
        | ^  16266 |   }
  16267 | 
  16268 | case HostRoot:
View compiled
performUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js:20285
  20282 |   startProfilerTimer(workInProgress);
  20283 | }
  20284 | 
> 20285 | next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
        | ^  20286 | workInProgress.memoizedProps = workInProgress.pendingProps;
  20287 | 
  20288 | if (workInProgress.mode & ProfileMode) {
View compiled
workLoop
node_modules/react-dom/cjs/react-dom.development.js:20326
  20323 | if (!isYieldy) {
  20324 |   // Flush work without yielding
  20325 |   while (nextUnitOfWork !== null) {
> 20326 |     nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
        | ^  20327 |   }
  20328 | } else {
  20329 |   // Flush asynchronous work until there's a higher priority event
View compiled
HTMLUnknownElement.callCallback
node_modules/react-dom/cjs/react-dom.development.js:147
  144 |     window.event = windowEvent;
  145 |   }
  146 | 
> 147 |   func.apply(context, funcArgs);
      | ^  148 |   didError = false;
  149 | } // Create a global error event handler. We use this to capture the value
  150 | // that was thrown. It's possible that this error handler will fire more
View compiled
invokeGuardedCallbackDev
node_modules/react-dom/cjs/react-dom.development.js:196
  193 | // errors, it will trigger our global error handler.
  194 | 
  195 | evt.initEvent(evtType, false, false);
> 196 | fakeNode.dispatchEvent(evt);
      | ^  197 | 
  198 | if (windowEventDescriptor) {
  199 |   Object.defineProperty(window, 'event', windowEventDescriptor);
View compiled
invokeGuardedCallback
node_modules/react-dom/cjs/react-dom.development.js:250
  247 | function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
  248 |   hasError = false;
  249 |   caughtError = null;
> 250 |   invokeGuardedCallbackImpl$1.apply(reporter, arguments);
      | ^  251 | }
  252 | /**
  253 |  * Same as invokeGuardedCallback, but instead of returning an error, it stores
View compiled
replayUnitOfWork
node_modules/react-dom/cjs/react-dom.development.js:19509
  19506 | 
  19507 | isReplayingFailedUnitOfWork = true;
  19508 | originalReplayError = thrownValue;
> 19509 | invokeGuardedCallback(null, workLoop, null, isYieldy);
        | ^  19510 | isReplayingFailedUnitOfWork = false;
  19511 | originalReplayError = null;
  19512 | 
View compiled
renderRoot
node_modules/react-dom/cjs/react-dom.development.js:20439
  20436 | if (true && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
  20437 |   if (mayReplay) {
  20438 |     var failedUnitOfWork = nextUnitOfWork;
> 20439 |     replayUnitOfWork(failedUnitOfWork, thrownValue, isYieldy);
        | ^  20440 |   }
  20441 | } // TODO: we already know this isn't true in some cases.
  20442 | // At least this shows a nicer error message until we figure out the cause.
View compiled
performWorkOnRoot
node_modules/react-dom/cjs/react-dom.development.js:21363
  21360 |   cancelTimeout(timeoutHandle);
  21361 | }
  21362 | 
> 21363 | renderRoot(root, isYieldy);
        | ^  21364 | finishedWork = root.finishedWork;
  21365 | 
  21366 | if (finishedWork !== null) {
View compiled

Another attempt:

const options = abs_for_codes.map((title) => {
    <option key={title}
    value={id} />
}

This doesn't work either - I tried it because it looks similar to the react arrays instructions.

The attached image shows the data structure in firestore.

NEXT ATTEMPT

Using Murray's suggestion, I have tried

import Select from "react-select";
import { fsDB, firebase, settings } from "../../../firebase";

let options = [];

const initialValues = {
  fieldOfResearch: null,

}


class ProjectForm extends React.Component {
  state = {
    selectedValue1: options,
}

handleSelectChange1 = selectedValue1 => {
    this.setState({ selectedValue1 });
  };


componentDidMount() {
    fsDB.collection("abs_for_codes").get().then(function (querySnapshot) {
        let newOptions = [];
        querySnapshot.forEach(function (doc) {
            console.log(doc.id, ' => ', doc.data());
            newOptions.push({
              value: doc.data().title.replace(/( )/g, ''),
              label: doc.data().title + ' - ABS ' + doc.id
            });
        });
        this.setState({options: newOptions});
    });
}


handleSubmit = (formState, { resetForm }) => {
    // Now, you're getting form state here!
    console.log("SUCCESS!! :-)\n\n", formState);
    fsDB
      .collection("project")
      .add(formState)
      .then(docRef => {
        console.log("docRef>>>", docRef);
        this.setState({ selectedValue1: null });
        this.setState({ selectedValue2: null });
        this.setState({ selectedValue3: null });
        this.setState({ selectedValue4: null });
        this.setState({ selectedValue5: null });
        this.setState({ selectedValue6: null });

        resetForm(initialValues);
      })
      .catch(error => {
        console.error("Error adding document: ", error);
      });
  };


onSubmit={this.handleSubmit}
        render={({ errors, status, touched, setFieldTouched, handleSubmit, values }) => {
          let fieldOfResearch;
          const handleChange1 = optionsObject => {
            fieldOfResearch = optionsObject;
            return (values.fieldOfResearch = optionsObject.value);
          };

<div className="form-group">
                                <label htmlFor="fieldOfResearch">
                                Select your field(s) of research
                                </label>

                                <Select
                                key=
{`my_unique_select_key__${fieldOfResearch}`}
                                name="fieldOfResearch"
                                isMulti
                                className={
                                    "react-select-container" +
                                    (errors.fieldOfResearch && touched.fieldOfResearch ? " is-invalid" : "")
                                }
                                classNamePrefix="react-select"
                                value={this.state.selectedValue1}
                                onChange={e => {
                                    handleChange1(e);
                                    this.handleSelectChange1(e);
                                }}
                                onBlur={setFieldTouched}
                                options={options}
                                />
                                {errors.fieldOfResearch && touched.fieldOfResearch && 
                                <ErrorMessage
                                name="fieldOfResearch"
                                component="div"
                                className="invalid-feedback d-block"
                                />}
                                </div>

So, stepping that through, options starts as an empty array, the ComponentDidMount function resets its state to NewOptions and that gets fed into the form select drop down.

That all makes sense to me, but it doesn't work - I just get an empty array.

When I try Avanthika's suggestion, i can render the form and multiple options can be selected from the right db collection, but nothing happens when I submit the form. The console debugger in react shows an unsmiling face (I've never seen that before. Pic below). This form submits fine when I remove the select field.

next attempt

when i try each of Murray R and Avinthika's updated suggestions below I can choose multiple fields. BUT i cannot submit the form. The form submits if i remove the select field. Is there a trick to submitting formik multi field forms?

My submit button is:

<div className="form-group">
                <Button
                  variant="outline-primary"
                  type="submit"
                  style={style3}
                  id="ProjectId"
                  onClick={handleSubmit}
                  disabled={!dirty || isSubmitting}

                >
                  Save
                </Button>
              </div>

My handle submit has:

handleSubmit = (formState, { resetForm }) => {
    // Now, you're getting form state here!
    console.log("SUCCESS!! :-)\n\n", formState);
    fsDB
      .collection("project")
      .add({
          ...(formState),
          createdAt: firebase.firestore.FieldValue.serverTimestamp()
      })
      .then(docRef => {
        console.log("docRef>>>", docRef);
        this.setState({ selectedValue1: null, selectedValue2: null, selectedValue3: null, selectedValue4: null, selectedValue5: null, selectedValue6: null });

        // this.setState({ selectedValue1: null });
        // this.setState({ selectedValue2: null });
        // this.setState({ selectedValue3: null });
        // this.setState({ selectedValue4: null });
        // this.setState({ selectedValue5: null });
        // this.setState({ selectedValue6: null });

        resetForm(initialValues);
      })
      .catch(error => {
        console.error("Error adding document: ", error);
      });
  };

The console doesn't log anything.

next attempt

I removed and reinstalled the react chrome extension and that's working again.

The attached screen shot shows the form isn't validating and isn't submitting, but the state of each of the form values is in there - you can see the bottom of the shot shows one of the form field values as 's'.

further attempt

So - I split this form out into a form that only has one field- the select field that I have been trying to work on here.

That form, in its entirety, has:

import React from 'react';

import { Formik, Form, Field, ErrorMessage, withFormik } from "formik";
import * as Yup from "yup";
import Select from "react-select";
import { fsDB, firebase, settings } from "../../../firebase";

import {
    Badge,
    Button,
    Col,
    ComponentClass,
    Feedback,
    FormControl,
    FormGroup,
    FormLabel,
    InputGroup,
    Table,
    Row,
    Container
  } from "react-bootstrap";

const initialValues = {
    fieldOfResearch: null,
}


class ProjectForm extends React.Component {
    state = {
      options: [],  
      selectedValue1: [],
    }

    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
            });
          }


  handleSelectChange1 = selectedValue1 => {
    this.setState({ selectedValue1 });
  };

  handleSubmit = (formState, { resetForm }) => {
    // Now, you're getting form state here!
    console.log("SUCCESS!! :-)\n\n", formState);
    fsDB
      .collection("project")
      .add({
          ...(formState),
          createdAt: firebase.firestore.FieldValue.serverTimestamp()
      })
      .then(docRef => {
        console.log("docRef>>>", docRef);
        this.setState({ selectedValue1: null});


        resetForm(initialValues);
      })
      .catch(error => {
        console.error("Error adding document: ", error);
      });
  };

  render() {
    const { options } = this.state;  
    return (
      <Formik
        initialValues={initialValues}
        validationSchema={Yup.object().shape({
          //   fieldOfResearch: Yup.array().required("What is your field of research?"),
        })}

        onSubmit={this.handleSubmit}
        render={({ errors, status, touched, setFieldTouched, handleSubmit, isSubmitting, dirty, values }) => {
          let fieldOfResearch;
          const handleChange1 = optionsObject => {
            fieldOfResearch = optionsObject;
            return (values.fieldOfResearch = optionsObject.value);
          };
          return (
            <div>
            <Form>
                <div className="form-group">

                                <label htmlFor="fieldOfResearch">
                                Select your field(s) of research
                                </label>
                                <Select
                                    key={`my_unique_select_key__${fieldOfResearch}`}
                                    name="fieldOfResearch"
                                    isMulti
                                    className={
                                        "react-select-container" +
                                        (errors.fieldOfResearch && touched.fieldOfResearch
                                        ? " is-invalid"
                                        : "")
                                    }
                                    classNamePrefix="react-select"
                                    value={this.state.selectedValue1}
                                    onChange={e => {
                                        handleChange1(e);
                                        this.handleSelectChange1(e);
                                    }}
                                    onBlur={setFieldTouched}
                                    options={options}
                                    />    

                                {errors.fieldOfResearch && touched.fieldOfResearch && 
                                <ErrorMessage
                                name="fieldOfResearch"
                                component="div"
                                className="invalid-feedback d-block"
                                />}
                                </div> 
                                <div className="form-group">
                                <Button
                                  variant="outline-primary"
                                  type="submit"
                                  id="ProjectId"
                                  onClick={handleSubmit}
                                //   disabled={!dirty || isSubmitting}

                                >
                                  Save
                                </Button>
                              </div>
                              </Form>

            </div>
        );
    }}
  />
);
}
}

export default ProjectForm;

This form allows the selection of a field of research in the form. The on submit function works in the console, to the extent that it logs success with a fieldOfResearch as 'undefined'. Nothing persists to the database.

The error message says: Unhandled Rejection (FirebaseError): Function DocumentReference.set() called with invalid data. Unsupported field value: undefined (found in field fieldOfResearch) ▶

When I try to enter a field value and inspect the react value, the error message says:

Uncaught TypeError: Cannot convert undefined or null to object


回答1:


Another updated answer:

The error message says: Unhandled Rejection (FirebaseError): Function DocumentReference.set() called with invalid data. Unsupported field value: undefined (found in field fieldOfResearch

This error happened because your form values not valid. You are not maintaining proper formik state.

I just tried this and checked, form submit works great for me. You've written too much of excess code - we just need native formik methods and firebase. The change log is as follows:

  1. The onChange of react-select should use setFieldValue from Formik like this:
onChange={selectedOptions => {
   // Setting field value - name of the field and values chosen.
   setFieldValue("fieldOfResearch", selectedOptions)}
}
  1. The initial value should be an empty array. Since we have initialValues declared and the formvalues maintained via Formik, there's absolutely no need for internal state management. I.E, there's no need for this.state.selectedValue1, handleChange1 and handleSelectChange1. If you take a look at the render() of your Formik HOC, you'll notice values - This gives current value of the form after every change.

So,

value={this.state.selectedValue1}

should be changed to

value={values.fieldOfResearch}
  1. I've written the handleSubmit like this - The exact replica of your code. But I'm only extracting values from the array of selected options:
handleSubmit = (formState, { resetForm }) => {
  // Now, you're getting form state here!
  const fdb = firebase.firestore();
  const payload = {
    ...formState,
    fieldOfResearch: formState.fieldOfResearch.map(t => t.value)
  }
  console.log("formvalues", payload);
  fdb
  .collection("project")
  .add(payload)
  .then(docRef => {
    console.log("docRef>>>", docRef);
    resetForm(initialValues);
  })
  .catch(error => {
    console.error("Error adding document: ", error);
  });
}

I'm able to see the form submission & the docRef in the console. The form also gets reset to initial state.

import React from "react";
import { Formik, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
import Select from "react-select";
import firebase from "./firebase";
import {
  Button,
  Container
} from "react-bootstrap";

const initialValues = {
  fieldOfResearch: []
};

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      options: []
    };
  }

  async componentWillMount() {
    const fdb = firebase.firestore();
    let options = [];
    await fdb
      .collection("abs_codes")
      .get()
      .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
          options.push({
            value: doc.data().title.replace(/( )/g, ""),
            label: doc.data().title
          });
        });
      });
    this.setState({
      options
    });
  }

  handleSubmit = (formState, { resetForm }) => {
    // Now, you're getting form state here!
    const fdb = firebase.firestore();
    const payload = {
      ...formState,
      fieldOfResearch: formState.fieldOfResearch.map(t => t.value)
    }
    console.log("formvalues", payload);
    fdb
    .collection("project")
    .add(payload)
    .then(docRef => {
      console.log("docRef>>>", docRef);
      resetForm(initialValues);
    })
    .catch(error => {
      console.error("Error adding document: ", error);
    });
  }

  render() {
    const { options } = this.state;
    return (
      <Container>
        <Formik
          initialValues={initialValues}
          validationSchema={Yup.object().shape({
              fieldOfResearch: Yup.array().required("What is your field of research?"),
          })}
          onSubmit={this.handleSubmit}
          render={({
            errors,
            status,
            touched,
            setFieldValue,
            setFieldTouched,
            handleSubmit,
            isSubmitting,
            dirty,
            values
          }) => {
            return (
              <div>
                <Form>
                  <div className="form-group">
                    <label htmlFor="fieldOfResearch">
                      Select your field(s) of research
                    </label>
                    <Select
                      key={`my_unique_select_keyfieldOfResearch`}
                      name="fieldOfResearch"
                      isMulti
                      className={
                        "react-select-container" +
                        (errors.fieldOfResearch && touched.fieldOfResearch
                          ? " is-invalid"
                          : "")
                      }
                      classNamePrefix="react-select"
                      value={values.fieldOfResearch}
                      onChange={selectedOptions => {
                        setFieldValue("fieldOfResearch", selectedOptions)}
                      }
                      onBlur={setFieldTouched}
                      options={options}
                    />
                    {errors.fieldOfResearch && touched.fieldOfResearch &&
                      <ErrorMessage
                         name="fieldOfResearch"
                         component="div"
                         className="invalid-feedback d-block"
                      />
                    }
                  </div>
                  <div className="form-group">
                    <Button
                      variant="outline-primary"
                      type="submit"
                      id="ProjectId"
                      onClick={handleSubmit}
                      disabled={!dirty || isSubmitting}
                    >
                      Save
                    </Button>
                  </div>
                </Form>
              </div>
            );
          }}
        />
      </Container>
    );
  }
}

export default App;

Just try copy pasting this first, and on top of this, try making your changes. I guess this should be helpful for you!


Updated Answer:

Hi Mel, I just set the whole thing in my system and tried doing it for you, although I cannot share the creds with you, I guess this should help.

  1. Javascript is not synchronous. Your componentDidMount will not wait for the data you're trying to get from firebase. It will just set the state before your query returns response.

  2. They key is to await the response. I've edited the code that way, and I'm able to see the options on my console in the render().

import React from 'react';
import firebase from "./firebase.js";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.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
        });
      });
    });
    this.setState({
      options
    });
  }

  render() {
    console.log(this.state);
    const { options } = this.state;
    return (
      <div className="form-group">
        <label htmlFor="fieldOfResearch">
          Select your field(s) of research
        </label>

        <Select
          key={`my_unique_select_key__${fieldOfResearch}`}
          name="fieldOfResearch"
          isMulti
          className={
            "react-select-container" +
            (errors.fieldOfResearch && touched.fieldOfResearch
              ? " is-invalid"
              : "")
          }
          classNamePrefix="react-select"
          value={this.state.selectedValue1}
          onChange={e => {
            handleChange1(e);
            this.handleSelectChange1(e);
          }}
          onBlur={setFieldTouched}
          options={options}
        />
      </div>
    );
  }
}

export default App;

Let me know if this works for you!

And I couldn't help but notice, why so many setStates in handleSubmit? You're forcing your component to rerender that many times. Instead you can do:

handleSubmit = (formState, { resetForm }) => {
    // Now, you're getting form state here!
    console.log("SUCCESS!! :-)\n\n", formState);
    fsDB
      .collection("project")
      .add(formState)
      .then(docRef => {
        console.log("docRef>>>", docRef);
        this.setState({ selectedValue1: null, selectedValue2: null, selectedValue3: null, selectedValue4: null, selectedValue5: null, selectedValue6: null });
        resetForm(initialValues);
      })
      .catch(error => {
        console.error("Error adding document: ", error);
      });
  };




回答2:


So would something like this help?

function SomeComponentName(props) {
  const [options, setOptions] = React.useState([]);

  React.useEffect(() => {
    getOptions()
  }, []}

  async function getOptions() {
    const tmpArr = [];

    try {
      // Perform get() request and loop through all docs
      await fsDB
        .collection("abs_codes")
        .get()
        .then(snapshot => {
          snapshot.forEach(doc => {
            const { title } = doc.data();
            const label = `${title} - ABS ${doc.key}`;

            tmpArr.push({ value: title, label });
          });
          setOptions(tmpArr);
        });
    } catch (err) {
      console.log("Error getting documents", err);
    }
  }

  return (
    <div className="form-group">
      <label>
        <Select
          // ...
          options={options}
          // ...
        />
      </label>
    </div>
  );
}

This will get all documents within the 'abs_code' collection, loop through them, and push each entry as an object to the 'options' array.




回答3:


For your React component to update after retrieving your research fields from Firestore, you'll need to declare your options in a way that React will pay attention to its value changes. You can do this by saving options within the class's state and update it later using setState():

// At the top of the class,
state = {
  options: [],
  // Any other state properties,
};

Then, within the componentDidMount() function, make your call to your Firestore collection and populate state.options with the results:

fsDB.collection("abs_for_codes").get().then(function (querySnapshot) {
    let newOptions = [];
    querySnapshot.forEach(function (doc) {
        console.log(doc.id, ' => ', doc.data());
        newOptions.push({
          value: doc.data().title.replace(/( )/g, ''),
          label: doc.data().title + ' - ABS ' + doc.id
        });
    });
    setState({options: newOptions});
});

This should retrieve your 'abs_for_codes' documents from Firestore and assign them to your component's options property in a way that will allow your Select element to be populated with the new data.



来源:https://stackoverflow.com/questions/55987388/populate-react-select-options-array-with-key-value-pairs-from-firebase-collectio

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!