微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

当我在不同组件的输入框中键入时,React Mapbox 组件闪烁

如何解决当我在不同组件的输入框中键入时,React Mapbox 组件闪烁

我想在一个页面上有一个地图和一个输入表单。用户应该能够在输入字段中输入内容,点击提交,然后让 MapBox 组件移动到该位置。

我有两个问题,我怀疑它们是相关的。当我在输入框中键入时,地图组件在每个 onChange 事件后闪烁。其次,当我提交表单时,地理定位成功获取,地图组件上的道具更新并出现地图标记,但地图没有执行我在进入 React Dev 工具和手动时看到的整洁动画更新道具。

我知道我是一个等级初学者,我敢肯定这有上千个问题。如果有人知道这个问题的答案,我会很高兴,但如果有人能指出我正确的方向,我也会很高兴。我花了一个上午的时间查看 React 的“入门”和“钩子”部分,并学习了一些 React 课程,您可以在其中构建简单的应用程序,但显然有一些基本的东西没有被理解。谢谢!

这是 NextJS 页面

import ReactMapBoxGl,{ Marker } from 'react-mapBox-gl';
import 'mapBox-gl/dist/mapBox-gl.css';
import { useState } from 'react';

export default function TestPage() {
  const Map = ReactMapBoxGl({
    accesstoken: process.env.NEXT_PUBLIC_MAPBox,});

  const [searchLocation,setSearchLocation] = useState('');
  const [geolocate,setGeolocate] = useState({
    coordinates: [2.61878695312962,47.8249046208979],isPin: false,});

  async function fetchGeolocate(location) {
    const url = `https://api.mapBox.com/geocoding/v5/mapBox.places/${location}.json?access_token=${process.env.NEXT_PUBLIC_MAPBox}`;
    fetch(url)
      .then((response) => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.text();
      })
      .then((myText) => {
        const json = JSON.parse(myText);
        const coordinates = json.features[0].center;
        setGeolocate({ coordinates,isPin: true });
      });
  }

  function handleInputChange(event) {
    setSearchLocation(event.target.value);
  }
  function handleForm(e) {
    e.preventDefault();
    fetchGeolocate(searchLocation);
  }

  return (
    <>
      <Map
        style="mapBox://styles/mapBox/streets-v11"
        containerStyle={{
          height: '50vh',width: '100vw',}}
        center={geolocate.coordinates}
        zoom={[3]}
      >
        {geolocate.isPin && (
          <Marker coordinates={geolocate.coordinates}>
            <span className="text-3xl" role="img" aria-label="push-pin">
              ?
            </span>
          </Marker>
        )}
      </Map>

      <form onSubmit={handleForm}>
        <div className="flex flex-col mb-4">
          <label
            htmlFor="search"
            className="flex flex-col uppercase font-bold text-lg text-gray-700"
          >
            Location
            <input
              type="text"
              name="search"
              placeholder="Location"
              value={searchLocation}
              className="border py-2 px-3 text-gray-700 block"
              onChange={handleInputChange}
            />
          </label>
        </div>
        <button type="submit">Find</button>
      </form>
    </>
  );
}

解决方法

您对问题相关的感觉是正确的。一个问题是您在每次渲染时都重新初始化 Map 组件:

const Map = ReactMapboxGl({
    accessToken: process.env.NEXT_PUBLIC_MAPBOX,});

它还将阻止动画发生(我认为),因为它会创建 Map 的新实例,这将导致 React 卸载/重新安装组件。

编辑:下面来自 OP 的回答:在组件外初始化

看起来你可以在任何渲染逻辑之外初始化组件。

替代方法:使用状态

在功能组件中,我们希望将初始化逻辑放在 useEffect 中(或者如果它发生在第一次渲染之前useLayoutEffect 中至关重要)。

const [map,setMap] = useState()
useEffect(() => {
  const mapInit = ReactMapboxGl({
    accessToken: process.env.NEXT_PUBLIC_MAPBOX,})
  setMap(mapInit)
},[])

末尾的 [] 是关键。它可以防止我们的回调在第一次之后被调用。

老实说,您的组件可能复杂到足以保证使用类组件,您可以在其中使用 componentDidMount 进行初始化,并且所有这些辅助函数不必在每次渲染时重新计算。最终取决于你。

最后一点:您已将 fetchGeolocate 声明为 async,这意味着您可以根据需要使用 .then,而不是对所有结果调用 await。或者拿走async并继续使用Promise.then,这取决于您。 Mozilla's docs 可能有助于澄清差异。

,

正如@elhil 上面所说,问题的根本在于我在每次渲染时重新初始化地图。所需要的只是将地图组件初始化从主函数中移出。我还接受了@elhil 的建议并修复了我的异步等待功能。代码如下。

import ReactMapboxGl,{ Marker } from 'react-mapbox-gl';
import 'mapbox-gl/dist/mapbox-gl.css';
import { useState } from 'react';

const Map = ReactMapboxGl({
  accessToken: process.env.NEXT_PUBLIC_MAPBOX,});

export default function TestPage() {
  const [searchLocation,setSearchLocation] = useState('');
  const [geolocate,setGeolocate] = useState({
    coordinates: [2.61878695312962,47.8249046208979],isPin: false,});

  async function fetchGeolocate(location) {
    const url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${location}.json?access_token=${process.env.NEXT_PUBLIC_MAPBOX}`;
    const response = await fetch(url);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const json = await response.json();
    const coordinates = json.features[0].center;
    setGeolocate({ coordinates,isPin: true });
  }

  function handleInputChange(event) {
    setSearchLocation(event.target.value);
  }
  function handleForm(e) {
    e.preventDefault();
    fetchGeolocate(searchLocation);
  }

  return (
    <>
      <Map
        style="mapbox://styles/mapbox/streets-v11"
        containerStyle={{
          height: '50vh',width: '100vw',}}
        center={geolocate.coordinates}
        zoom={[3]}
      >
        {geolocate.isPin && (
          <Marker coordinates={geolocate.coordinates}>
            <span className="text-3xl" role="img" aria-label="push-pin">
              ?
            </span>
          </Marker>
        )}
      </Map>

      <form onSubmit={handleForm}>
        <div className="flex flex-col mb-4">
          <label
            htmlFor="search"
            className="flex flex-col uppercase font-bold text-lg text-gray-700"
          >
            Location
            <input
              type="text"
              name="search"
              placeholder="Location"
              value={searchLocation}
              className="border py-2 px-3 text-gray-700 block"
              onChange={handleInputChange}
            />
          </label>
        </div>
        <button type="submit">Find</button>
      </form>
    </>
  );
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。