Search code examples
reactjsreact-bootstrap-typeahead

How to get the id rather than labelKey from typeahead when an array of objects is passed through?


I have my react-bootstrap-typeahead setup like this:

// MusicServices.jsx
import React, {Fragment, useState} from 'react';
import { Form } from 'react-bootstrap';
import { Typeahead } from 'react-bootstrap-typeahead';

const MusicServices = ({services}) => {
  const [musicServiceID, setMusicServiceSelection] = useState([]);
  const [transferDetailID, setTransferDetailSelection] = useState([]);

  const musicServiceDetails = Object.keys(services).map((serviceID) => {
    return {
        id: serviceID,
        name: services[serviceID].name
    }
  });

  const getTransferDetails = () => {
    let transferDetailSelections = []
    if (musicServiceID.length) {
      transferDetailSelections = services[musicServiceID[0].id].transferDetails;
    }
    return (
      <Form.Group>
         <Form.Label>Transfer Details</Form.Label>
         <Typeahead
          name="transferdetail"
          id="transfer-detail"
          labelKey="name"
          onChange={setTransferDetailSelection}
          options={transferDetailSelections}
          placeholder="Choose a Transfer Detail..."
          selected={transferDetailID}
         />
       </Form.Group>
    )
  }
  return (
    <Fragment>
      <Form.Group>
        <Form.Label>Music Services</Form.Label>
        <Typeahead
          name="musicservice"
          id="music-service"
          labelKey="name"
          onChange={(selected) => {
            setMusicServiceSelection(selected)
          }}
          options={musicServiceDetails}
          placeholder="Choose a music service..."
          selected={musicServiceID}
        />
      </Form.Group>
      {getTransferDetails()}
    </Fragment>
  );
};

export default MusicServices;

I use the same code ina. few places so I extracted it out to it's own function...

SO I use it in a form like this:

import React from 'react';
const Jumbotron = require('react-bootstrap').Jumbotron;
const Container = require('react-bootstrap').Container;
import Form from 'react-bootstrap/Form';
const Button = require('react-bootstrap').Button;

import MusicServices from '../../components/auth/MusicServices';


const Generate = ({services}) => {
  const handleSubmit = (event) => {
    const formEvent = event.currentTarget;
    console.log(event.target.elements)

    event.preventDefault();
    event.stopPropagation();
  };

  let mservices = {123455: {name: 'abc', transferDetails: [{id: 6789, name: 'der'}]}}

  return (
    <>
      <Jumbotron>
        <Container>
          <h1 className="display-3">Generate</h1>
          <p>{session.user.name}, this will generate a new key pair to be stored on the filestore and against a transfer detail.</p>
        </Container>
      </Jumbotron>
      <Container>
        <Form onSubmit={handleSubmit}>
          <Form.Group controlId="service">
            <MusicServices services={mservices}></MusicServices>
          </Form.Group>
          <Button variant="primary" type="submit">
            Generate key pair
          </Button>
        </Form>
      </Container>
    </>
  )
}

export default Generate

In my handleSubmit() function, I want to get the value of the transfer-detail typeahead, but the value is coming back as "der". What I really want is the "6789" id value, with the "der" being displayed to the user to typeahead on.

I can't see how to get this value. At a guess it's in transferDetailID, but that doesn't seem available in my Generate.jsx page.


Solution

  • The useState hooks need to be in the parent and passed down to the child, so in this instance, they need to be taken from the MusicServices functional Component and put into the Generate functional Component.

    You then have to pass both the variable and the setter through to the child. So in the Generate functional component I have changed the code:

    const Generate = ({services}) => {
      const [musicServiceID, setMusicServiceSelection] = useState([]);
      const [transferDetailID, setTransferDetailSelection] = useState([]);
      return (
        <>
          ...
          <Container>
            <Form onSubmit={handleSubmit}>
              <Form.Group controlId="service">
                {
                  MusicServices(
                    services,
                    musicServiceID,
                    setMusicServiceSelection,
                    transferDetailID,
                    setTransferDetailSelection
                  )
                }
              </Form.Group>
            </Form>
           </Container>
           ...
        </>
      )
    };
    
    

    this then allows the handleSubmit function access to the states of musicServiceID and transferDetailID.