0

I was trying to incorporate an input address autofill inside a modal. The autofill suggestions appear, but when I try to click/select the preferred address an error is occurring and the modal closes. I manually coded the modal. No styling library used.

This is my modal code:

import { cloneElement, createContext, useContext, useState } from 'react';
import { useClickOutside } from '../hooks/useClickOutside';
import { createPortal } from 'react-dom';
import { HiXMark } from 'react-icons/hi2';

const ModalContext = createContext();

function Modal({ children }) {
  const [openName, setOpenName] = useState('');

  const close = () => setOpenName('');
  const open = setOpenName;

  return (
    <ModalContext.Provider value={{ openName, close, open }}>
      {children}
    </ModalContext.Provider>
  );
}

function Open({ children, opens: opensWindowName }) {
  const { open } = useContext(ModalContext);

  // return cloneElement(children, { onClicsk: () => console.log('oyeah') });
  return cloneElement(children, { onClick: () => open(opensWindowName) });
}

function Window({ children, name }) {
  const { openName, close } = useContext(ModalContext);

  const ref = useClickOutside(close);

  if (name !== openName) return null;

  // createPortal set the element to the desired parent element
  return createPortal(
    <div className="fixed left-0 top-0 z-[1000] h-full w-full backdrop-blur-sm transition-all">
      <div
        className="fixed left-[50%] top-[50%] -translate-x-1/2 -translate-y-1/2 rounded-md bg-grey-0 px-8 py-6 shadow-lg transition-all"
        ref={ref}
      >
        <button className="absolute right-1 top-1 translate-x-px translate-y-px rounded-md p-1 text-grey-500 transition-all hover:bg-grey-100">
          <HiXMark onClick={close} className="h-6 w-6" />
        </button>
        {cloneElement(children, { onCloseModal: close })}
      </div>
    </div>,
    document.body,
  );
}

Modal.Open = Open;
Modal.Window = Window;

export default Modal;

This is my create deployment code:

import { useForm } from 'react-hook-form';
import FormRow from '../../ui/FormRow';
import { useCreateDeployment } from './useCreateDeployment';
import { useEditDeployment } from './useEditDeployment';
import { DayPicker, getDefaultClassNames } from 'react-day-picker';
import { useEffect, useState } from 'react';
import { add } from 'date-fns';
import 'react-day-picker/dist/style.css';
import { AddressAutofill } from '@mapbox/search-js-react';

function CreateDeployment({ deploymentToEdit = {}, onCloseModal }) {
  const [selected, setSelected] = useState({ from: undefined, to: undefined });
  const maxDate = add(new Date(), { years: 1 });
  const { from: fromDate, to: toDate } = selected;

  // console.log(deploymentToEdit);

  const handleSelect = (selectedRange) => {
    if (selectedRange === undefined) return;
    setSelected(selectedRange);
  };

  const { isCreating, createDeployment } = useCreateDeployment();
  const { isEditing, editDeployment } = useEditDeployment();
  const isWorking = isCreating || isEditing;

  const { deploymentId: editId, ...editValues } = deploymentToEdit;
  const isEditSession = Boolean(editId);

  const { register, handleSubmit, reset, formState } = useForm({
    defaultValues: isEditSession ? editValues : {},
  });

  const { errors } = formState;
  const defaultClassNames = getDefaultClassNames();

  function onSubmit(data) {
    // console.log(data);
    if (isEditSession)
      editDeployment(
        {
          newDeploymentData: { ...data },
          deploymentId: editId,
        },
        { onSuccess: (data) => reset() },
        onCloseModal?.(),
      );
    else
      createDeployment(
        { ...data, startDate: selected.from, endDate: selected.to },
        { onSuccess: (data) => reset() },
        onCloseModal?.(),
      );
  }

  return (
    <form
      className="max-h-100 w-[40rem] text-lg"
      onSubmit={handleSubmit(onSubmit)}
    >
      <div className="max-h-[40rem] overflow-y-auto">
        <FormRow
          label="Deployment Name"
          error={errors?.deploymentName?.message}
        >
          <input
            className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2 shadow-sm"
            {...register('deploymentName', {
              required: 'This field is required',
            })}
            disabled={isWorking}
          />
        </FormRow>
        <FormRow
          label="Number of Personnel"
          error={errors?.numberOfPersonnel?.message}
        >
          <input
            className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
            {...register('numberOfPersonnel', {
              required: 'This field is required',
            })}
          />
        </FormRow>
        <FormRow
          label="Number of Resources"
          error={errors?.numberOfResources?.message}
        >
          <input
            className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
            {...register('numberOfResources', {
              required: 'This field is required',
            })}
            disabled={isWorking}
          />
        </FormRow>
        <FormRow label="Location" error={errors?.location?.message}>
          {/* <input
            className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
            {...register('location', {
              required: 'This field is required',
            })}
            disabled={isWorking}
          /> */}
          <AddressAutofill accessToken="Token">
            <input
              className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
               name="address-1"
               autoComplete="address-line1"
            />
          </AddressAutofill>
        </FormRow>

        <div className="flex flex-col items-center border-b border-grey-100 pt-4">
          <div className="m-auto flex">
            <p className="mx-10 px-4 text-sm font-medium text-grey-700">
              From Date:{' '}
              {fromDate ? fromDate.toLocaleDateString() : 'Not selected'}
            </p>

            <p className="mx-10 px-10 text-sm font-medium text-grey-700">
              End Date: {toDate ? toDate.toLocaleDateString() : 'Not selected'}
            </p>
          </div>
          <DayPicker
            classNames={{
              root: `${defaultClassNames.root} scale-75 text-base font-medium text-grey-700 `,
              months: `flex gap-10`,
              range_start: `rounded-full bg-brand-500 opacity-2- text-grey-200`,
              range_end: `rounded-full bg-brand-500 opacity-2- text-grey-200`,
              today: `font-bold text-brand-500`,
              selected: `bg-brand-300 font-medium text-brand-200`,
              chevron: `${defaultClassNames.chevron} fill-brand-500`,
            }}
            mode="range"
            selected={selected}
            onSelect={handleSelect}
            startMonth={new Date()}
            endMonth={maxDate}
            captionLayout="dropdown-months"
            numberOfMonths={2}
            disabled={{
              before: new Date(),
              after: maxDate,
            }}
          />
        </div>

        <FormRow label="Status" error={errors?.status?.message}>
          <select
            className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
            {...register('status', {
              required: 'This field is required',
            })}
            disabled={isWorking}
          >
            <option value="Pending">Pending</option>
            <option value="Ongoing">Ongoing</option>
          </select>
        </FormRow>

        <FormRow label="OPR" error={errors?.opr?.message}>
          <input
            className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
            {...register('opr', {
              required: 'This field is required',
            })}
            disabled={isWorking}
          />
        </FormRow>
        <FormRow
          className="!bg-red-600"
          label="Description"
          error={errors?.description?.message}
        >
          <textarea
            className="h-28 w-56 rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
            {...register('description', {
              required: 'This field is required',
            })}
            disabled={isWorking}
          />
        </FormRow>

        <div className="flex justify-end">
          <button
            className="btn-secondary mx-2 mt-[auto]"
            type="reset"
            onClick={() => onCloseModal?.()}
            disabled={isWorking}
          >
            Cancel
          </button>
          <button
            type="submit"
            className="btn-primary mx-2 mt-5"
            disabled={isWorking}
          >
            Submit
          </button>
        </div>
      </div>
    </form>
  );
}

export default CreateDeployment;

This is the error I encounter everytime i try to click the suggested address:

enter image description here

I've tested the autofill directly into Createeployment component and it works, so only when using the Createdeployment inside a modal modal.

1 Answer 1

0

name and autoComplete attributes are required for Mapbox autofill component. Uncommenting these attributes would resolve the problem.

<AddressAutofill accessToken="Token">
  <input
    className="rounded-md border border-solid border-grey-300 bg-grey-0 px-4 py-2"
    name="address-1"
    autoComplete="address-line1"
  />
</AddressAutofill>

Sign up to request clarification or add additional context in comments.

3 Comments

Hi, sorry but I forgot to uncomment the line because I was trying some workaround that time but that is not the case, Even if it is uncommented the error still persists. Anyways, do you have any idea what the error is or can you explain what is happening, that would be a big help. I've tried all resources I know just to know what the error is but still no progress.. Thank you!
One of the possible reasons could be autofill component is not fully mounted before mapbox script tries to access it. Because of which it is null while setting any attribute using setattribute in mapbox script.
Check if modal is mounted before mapbox accesses it (use logging in use effect for this), or you can use ref instead which is a preferred way of working with mapbox things.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.