You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

模态框中使用Mapbox地址自动补全时出现TypeError错误的解决方法

问题:Mapbox地址自动补全在自定义模态框中点击选择时触发错误并关闭模态框

我手动实现了一个无第三方样式库依赖的模态框,在其中集成Mapbox地址自动补全功能后,地址建议列表可以正常显示,但点击选择地址时会触发以下错误:
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'setAttribute')
同时模态框会自动关闭。单独使用CreateDeployment组件时该功能完全正常,仅在模态框内使用时出现此问题。

模态框代码

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, { onClick: () => open(opensWindowName) });
}

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

  const ref = useClickOutside(close);

  if (name !== openName) return null;

  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;

CreateDeployment组件代码

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;

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

错误截图

错误截图


内容的提问来源于stack exchange,提问作者JungProg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 11:57:36