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

React JS注册页添加Google Map地址搜索与标记功能问题

React企业注册页:修复Google Map地址搜索与标记功能

问题核心

开发企业注册页时遇到两个关键问题:

  • 混用函数组件与类组件写法导致代码报错,无法实现地址搜索与标记功能
  • 希望将地址搜索栏嵌入Google Map组件内部,而非外部显示

错误原因分析

  1. 组件定义语法错误:尝试将函数组件改为类组件时,使用了错误写法const Register = React.Component,正确类组件定义应为class Register extends React.Component;更推荐延续原函数组件风格,配合React Hooks实现功能。
  2. 状态与引用混用:函数组件中不能使用类组件的this.state、this.setState和this实例引用,需替换为useState、useRef等Hooks API。
  3. 搜索框布局问题:原代码将搜索框放在地图外部,需通过CSS绝对定位将其嵌入地图容器内。

修复后的完整代码

import { useState, useEffect, useRef } from 'react';
import { GoogleMap, LoadScript, StandaloneSearchBox, Marker } from '@react-google-maps/api';

const initialState = {
  company: '',
  email: '',
  password: '',
  isMember: true,
};

const Register = () => {
  const [values, setValues] = useState(initialState);
  // 地图相关状态管理
  const [currentLocation, setCurrentLocation] = useState({ lat: -3.745, lng: -38.523 });
  const [markers, setMarkers] = useState([]);
  const [bounds, setBounds] = useState(null);
  
  // 获取地图和搜索框的引用
  const searchBoxRef = useRef(null);
  const mapRef = useRef(null);

  const containerStyle = {
    width: '400px',
    height: '400px',
    position: 'relative' // 为搜索框绝对定位提供父容器基准
  };

  // 组件挂载时获取当前地理位置
  useEffect(() => {
    navigator?.geolocation.getCurrentPosition(
      ({ coords: { latitude: lat, longitude: lng } }) => {
        setCurrentLocation({ lat, lng });
      }
    );
  }, []);

  // 地图加载完成后的逻辑处理
  const onMapLoad = (map) => {
    mapRef.current = map;
    map.addListener('bounds_changed', () => {
      setBounds(map.getBounds());
    });
  };

  // 保存搜索框实例引用
  const onSBLoad = (ref) => {
    searchBoxRef.current = ref;
  };

  // 地址搜索结果变化时生成标记并调整地图视野
  const onPlacesChanged = () => {
    const places = searchBoxRef.current.getPlaces();
    if (places.length === 0) return;

    const newMarkers = places.map(place => place.geometry.location);
    setMarkers(newMarkers);

    // 自动适配搜索结果的地图视野
    const newBounds = new window.google.maps.LatLngBounds();
    places.forEach(place => {
      if (place.geometry.viewport) {
        newBounds.union(place.geometry.viewport);
      } else {
        newBounds.extend(place.geometry.location);
      }
    });
    mapRef.current.fitBounds(newBounds);
  };

  const handleChange = (e) => {
    setValues(prev => ({ ...prev, [e.target.name]: e.target.value }));
  };

  const onSubmit = (e) => {
    e.preventDefault();
    console.log('注册提交信息:', values);
    console.log('选中的地址标记:', markers);
    // 此处可添加提交至后端的逻辑
  };

  return (
    <div className="page-template-default page page-id-13">
      <header className="site-header">
        <div className="container">
          <h1 className="school-logo-text float-left">
            <a href="/landing"><strong>Direct</strong> Connection</a>
          </h1>
          <div className="site-header__util"></div>
        </div>
      </header>

      <div className="page-banner">
        <div className="page-banner__bg-image" style={{ backgroundImage: "url('connection.jpg')" }}></div>
        <div className="page-banner__content container container--narrow">
          <h1 className="page-banner__title">Register</h1>
          <div className="page-banner__intro">
            <p></p>
          </div>
        </div>
      </div>

      <div className="container container--narrow page-section">
        <h2 className="headline headline--tiny">Want to join and connect with companies? Sign up and get your company out there:</h2>
        
        <form onSubmit={onSubmit}>
          <label className="header">Profile Photo:</label>
          <input
            id="image"
            type="file"
            name="profile_photo"
            placeholder="Photo"
            required
            capture
            onChange={handleChange}
          />

          <label className="company-name">Company Name</label>
          <div className="company-input">
            <input
              className="headline headline--input"
              type="text"
              name="company"
              placeholder="Enter Company"
              value={values.company}
              onChange={handleChange}
            />
          </div>

          <label className="company-email">Email</label>
          <div className="email-input">
            <input
              className="headline headline--input"
              type="email"
              name="email"
              placeholder="Enter Email"
              value={values.email}
              onChange={handleChange}
            />
          </div>

          <label className="company-map">Map</label>
          <div className="map-input">
            <LoadScript googleMapsApiKey='API HERE'>
              <div style={containerStyle}>
                {/* 嵌入地图内部的搜索框 */}
                <div style={{
                  position: 'absolute',
                  top: '10px',
                  left: '50%',
                  transform: 'translateX(-50%)',
                  zIndex: 1000,
                  width: '80%'
                }}>
                  <StandaloneSearchBox
                    onLoad={onSBLoad}
                    onPlacesChanged={onPlacesChanged}
                    bounds={bounds}
                  >
                    <input
                      type="text"
                      placeholder="Search Address"
                      style={{
                        width: '100%',
                        padding: '8px',
                        borderRadius: '4px',
                        border: '1px solid #ccc'
                      }}
                    />
                  </StandaloneSearchBox>
                </div>

                <GoogleMap
                  ref={mapRef}
                  mapContainerStyle={{ width: '100%', height: '100%' }}
                  center={currentLocation}
                  zoom={10}
                  onLoad={onMapLoad}
                >
                  {markers.map((mark, index) => (
                    <Marker key={index} position={mark} />
                  ))}
                </GoogleMap>
              </div>
            </LoadScript>
          </div>

          <button type="submit" style={{ marginTop: '20px', padding: '10px 20px' }}>
            Submit Registration
          </button>
        </form>
      </div>

      <footer className="site-footer">
        <div className="site-footer__inner container container--narrow">
          <div className="group">
            <div className="site-footer__col-one">
              <h1 className="school-logo-text school-logo-text--alt-color">
                <a href="/landing"><strong>Direct</strong> Connection</a>
              </h1>
              <p><a className="site-footer__link" href="index.html">706-263-0175</a></p>
            </div>
          </div>
        </div>
      </footer>
    </div>
  );
};

export default Register;

关键修复点说明

  1. 统一函数组件风格:延续原函数组件定义,使用useState管理状态、useRef获取地图和搜索框实例。
  2. 嵌入搜索框:通过CSS绝对定位将搜索框放置在地图容器内部,设置z-index确保搜索框显示在地图上方。
  3. 修复地理定位逻辑:使用useEffect在组件挂载时获取当前地理位置,替代类组件生命周期方法。
  4. 正确处理搜索结果:通过useRef获取搜索框实例,地址变化时生成标记并自动调整地图视野适配结果。
  5. 完善表单逻辑:为输入框添加name属性和onChange事件,同步表单状态到组件state,便于后续提交处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 04:15:39