-2

As the title says, i'm getting an unwanted page refresh whenever i move the draggable marker. I'd like to set map to pan to the new marker position every time but will settle for solving the page refresh problem at the moment.

I've added snippets of the pieces of code i think are responsible

Any help greatly appreciated

import React, { Component } from 'react';
import { withGoogleMap, GoogleMap, withScriptjs, InfoWindow, Marker } from "react-google-maps";
import { Mutation } from 'react-apollo';
import gql from 'graphql-tag';
import Geocode from "react-geocode";
import User from './User';
import Autocomplete from 'react-google-autocomplete';
import FormBlock from './styles/FormBlock'
import SickButton from './styles/SickButton';
import styled from 'styled-components';
import Form from './styles/Form';
import Error from './ErrorMessage';
import Router from 'next/router';
Geocode.setApiKey( "AIzaSyC7oaBUKbAULgrhMSPx-pT8zr2PG3lpOT4" );
Geocode.enableDebug();

const Mapbox = styled.div`
    margin: 0;
    padding: 10px;
    z-index: 3;
`;

const CREATE_JOB_MUTATION = gql`
  mutation CREATE_JOB_MUTATION(
    $address: String!
    $lat: Float
    $lng: Float
    $description: String!
    $image: String!
    $cube: Int!
    $reqPickup: String
    $instructions: String!
    $feedback: String
    $pickup: DateTime
    $charges: Int
    $price: Int
  ) {
    createJob(
    address: $address
    lat: $lat
    lng: $lng
    description: $description
    image: $image
    cube: $cube
    reqPickup: $reqPickup
    instructions: $instructions
    feedback: $feedback
    pickup: $pickup
    charges: $charges
    price: $price
    ) {
      id
      address
      lat
      lng
    }
  }
`;

class Map extends Component{
    constructor( props ){
        super( props );
        this.state = {
            address: '',
            lat: '',
            lng: '',
            city: '',
            area: '',
            state: '',
            image: '',
            cube: '',
            reqPickup: '',
            instructions: '',
            feedback: '',
            pickup: '',
            charges: '0',
            price: '0',
            mapPosition: {
                lat: this.props.center.lat,
                lng: this.props.center.lng
            },
            markerPosition: {
                lat: this.props.center.lat,
                lng: this.props.center.lng
            }
        };
        this.onPlaceSelected = this.onPlaceSelected.bind(this);
    }

    handleChange = (e) => {
        const { name, type, value } = e.target;
        const val = type === 'number' ? parseFloat(value) : value;
        this.setState({ [name]: val});
      };    



      uploadFile = async e => {
        const files = e.target.files;
        const data = new FormData();
        data.append('file', files[0]);
        data.append('upload_preset', 'sickfits');

        const res = await fetch('https://api.cloudinary.com/v1_1/wesbostutorial/image/upload', {
          method: 'POST',
          body: data,
        });
        const file = await res.json();
        this.setState({
          image: file.secure_url,
          largeImage: file.eager[0].secure_url,
        });
      };


    /**
     * Get the current address from the default map position and set those values in the state
     */
    componentDidMount() {
        Geocode.fromLatLng( this.state.mapPosition.lat , this.state.mapPosition.lng ).then(
            response => {
                const address = response.results[0].formatted_address,
                      addressArray =  response.results[0].address_components,
                      city = this.getCity( addressArray ),
                      area = this.getArea( addressArray ),
                      state = this.getState( addressArray );

                // console.log( 'city', city, area, state );

                this.setState( {
                    address: ( address ) ? address : '',
                    area: ( area ) ? area : '',
                    city: ( city ) ? city : '',
                    state: ( state ) ? state : '',
                    lat: this.state.markerPosition.lat,
                    lng: this.state.markerPosition.lng,
                } )
            },
            error => {
                console.error( error );
            }
        );
    };

    getCity = ( addressArray ) => {
        let city = '';
        for( let i = 0; i < addressArray.length; i++ ) {
            if ( addressArray[ i ].types[0] && 'administrative_area_level_2' === addressArray[ i ].types[0] ) {
                city = addressArray[ i ].long_name;
                return city;
            }
        }
    };
    /**
     * Get the area and set the area input value to the one selected
     *
     * @param addressArray
     * @return {string}
     */
    getArea = ( addressArray ) => {
        let area = '';
        for( let i = 0; i < addressArray.length; i++ ) {
            if ( addressArray[ i ].types[0]  ) {
                for ( let j = 0; j < addressArray[ i ].types.length; j++ ) {
                    if ( 'sublocality_level_1' === addressArray[ i ].types[j] || 'locality' === addressArray[ i ].types[j] ) {
                        area = addressArray[ i ].long_name;
                        return area;
                    }
                }
            }
        }
    };
    /**
     * Get the address and set the address input value to the one selected
     *
     * @param addressArray
     * @return {string}
     */
    getState = ( addressArray ) => {
        let state = '';
        for( let i = 0; i < addressArray.length; i++ ) {
            for( let i = 0; i < addressArray.length; i++ ) {
                if ( addressArray[ i ].types[0] && 'administrative_area_level_1' === addressArray[ i ].types[0] ) {
                    state = addressArray[ i ].long_name;
                    return state;
                }
            }
        }
    };
    /**
     * And function for city,state and address input
     * @param event
     */

    /**
     * This Event triggers when the marker window is closed
     *
     * @param event
     */
    onInfoWindowClose = ( event ) => {

    };

    /**
     * When the marker is dragged you get the lat and long using the functions available from event object.
     * Use geocode to get the address, city, area and state from the lat and lng positions.
     * And then set those values in the state.
     *
     * @param event
     */
    onMarkerDragEnd = ( event ) => {
        let newLat = event.latLng.lat(),
            newLng = event.latLng.lng();

        Geocode.fromLatLng( newLat , newLng ).then(
            response => {
                const address = response.results[0].formatted_address,
                      addressArray =  response.results[0].address_components,
                      city = this.getCity( addressArray ),
                      area = this.getArea( addressArray ),
                      state = this.getState( addressArray );
                this.setState( {
                    address: ( address ) ? address : '',
                    area: ( area ) ? area : '',
                    city: ( city ) ? city : '',
                    state: ( state ) ? state : '',
                    lat: this.state.markerPosition.lat,
                    lng: this.state.markerPosition.lng,
                    markerPosition: {
                        lat: newLat,
                        lng: newLng
                    },
                    mapPosition: {
                        lat: newLat,
                        lng: newLng
                    },
                } )
            },
            error => {
                console.error(error);
            }
        );
    };

    /**
     * When the user types an address in the search box
     * @param place
     */
    onPlaceSelected = ( place ) => {
        // console.log( 'plc', place );
        const address = place.formatted_address,
              addressArray =  place.address_components,
              city = this.getCity( addressArray ),
              area = this.getArea( addressArray ),
              state = this.getState( addressArray ),
              latValue = place.geometry.location.lat(),
              lngValue = place.geometry.location.lng();
        // Set these values in the state.
        this.setState({
            address: ( address ) ? address : '',
            area: ( area ) ? area : '',
            city: ( city ) ? city : '',
            state: ( state ) ? state : '',
            lat: this.state.markerPosition.lat,
            lng: this.state.markerPosition.lng,
            markerPosition: {
                lat: latValue,
                lng: lngValue
            },
            mapPosition: {
                lat: latValue,
                lng: lngValue
            },
        })
    };

    render(){
        const defaultMapOptions = {
            disableDefaultUI: true,
          };
        const AsyncMap = withScriptjs(
            withGoogleMap(
                props => (
                    <GoogleMap 
                            google={ this.props.google }
                               defaultZoom={ this.props.zoom }
                               defaultCenter={{ lat: this.state.mapPosition.lat, lng: this.state.mapPosition.lng }}
                               defaultOptions={defaultMapOptions}       
                    >

                        <Marker google={this.props.google}
                                name={''}
                                draggable={true}
                                onDragEnd={ this.onMarkerDragEnd }
                                position={{ lat: this.state.markerPosition.lat, lng: this.state.markerPosition.lng }}
                        />
                        <Marker />
                    </GoogleMap>

                )
            )
        );
        let map;
        if( this.props.center.lat !== undefined ) {
            map = <Mapbox>
                <AsyncMap 
                    googleMapURL="https://maps.googleapis.com/maps/api/js?key=AIzaSyC7oaBUKbAULgrhMSPx-pT8zr2PG3lpOT4&libraries=places"
                    loadingElement={
                        <div style={{ height: `100%` }} />
                    }
                    containerElement={
                        <div style={{ height: this.props.height, margin: '0px', padding: 'auto',}} />
                    }
                    mapElement={
                        <div style={{ height: `100%`, margin: '0px' }} />
                    }
                />
                <Mutation mutation={CREATE_JOB_MUTATION} variables={this.state}>
                        {(createJob, { loading, error }) => (
                        <Form
                        data-test="form"
                        onSubmit={async e => {
                            // Stop the form from submitting
                            e.preventDefault();
                            // call the mutation
                            this.setState({ address: this.state.address, lat: this.state.markerPosition.lat, lng: this.state.markerPosition.lng });
                            console.log(this.state);
                            const res = await createJob();
                            // change them to the single job page
                            Router.push({
                            pathname: '/orderprocess',
                            query: { id: res.data.createJob.id },
                            });
                        }}>
                <Error error={error} />
            <fieldset disabled={loading} aria-busy={loading}>

                    <label htmlFor="address">
                      <input
                        type="text"
                        id="address"
                        name="address"
                        placeholder="address"
                        required
                        readOnly value={this.state.address}
                        onChange={this.handleChange}
                      />
                    </label>

                    <label htmlFor="lat">
                      <input
                        type="number"
                        id="lat"
                        name="lat"
                        placeholder="lat"
                        required
                        readOnly value={ this.state.markerPosition.lat}
                        onChange={this.handleChange}
                      />
                    </label>

                    <label htmlFor="lng">
                      <input
                        type="number"
                        id="lng"
                        name="lng"
                        placeholder="lng"
                        required
                        readOnly value={this.state.markerPosition.lng}
                        onChange={this.handleChange}
                      />
                    </label>

                {/* <label htmlFor="description"> 
                    Describe your waste: Just a few words to describe the materials.
                    <input
                    type="text"
                    id="description"
                    name="description"
                    placeholder="eg: wood, bricks, old kitchen tops and a fridge"
                    required
                    value={this.state.description}
                    onChange={this.handleChange}
                  />
                    </label>

                <label htmlFor="image" className="pic">
                    Image: make sure you get it all in shot from a couple of different angles.
                    <input
                    type="file"
                    id="image"
                    name="image"
                    placeholder="Upload an image"
                    required
                    onChange={this.uploadFile}
                    />
                    {this.state.image && (
                    <img width="200" src={this.state.image} alt="Upload Preview" />
                    )}
                </label>

                <label htmlFor="cube"> 
                    Cube: Have a a guess at cm3 of your waste. Don't worry if you get it wrong, its just to help us quote you accuratly.
                    <input
                    type="number"
                    id="cube"
                    name="cube"
                    placeholder="Estimate how many cubic metres your waste occupies"
                    required
                    value={this.state.cube}
                    onChange={this.handleChange}
                  />
                </label>

                <label htmlFor="reqPickup"> 
                    Required Pickup: Tell us an ideal time to collect/deliver.
                    <input
                    type="text"
                    id="reqPickup"
                    name="reqPickup"
                    placeholder="eg: asap "
                    required
                    value={this.state.reqPickup}
                    onChange={this.handleChange}
                  />
                </label>

                    <label htmlFor="instructions"> 
                    Instructions: Any specific instructions such as desired collection time or access info.
                    <input
                    type="text"
                    id="instructions"
                    name="instructions"
                    placeholder="Instructions for the collection team"
                    required
                    value={this.state.instructions}
                    onChange={this.handleChange}
                  />
                    </label> */}

                    <p
                    style={{
                        textAlign: 'center',
                      }}
                    >Submit your order and we'll come back to you in just a few moments with a quote and collection time options</p>
                    <div
                     style={{
                        display: 'flex',
                        margin: 'auto',
                        justifyContent: 'center',
                      }}
                    >
                    <button
                    type="submit">SUBMIT</button>
                    </div>
                </fieldset>
            </Form>
            )}
            </Mutation>


            </Mapbox>

        } else {
            map = <div style={{height: this.props.height}} />
        }
        return( map )
    }
}
export default Map
export { CREATE_JOB_MUTATION };
Chris Bell
  • 33
  • 1
  • 6

1 Answers1

0

When you say a page refresh, do you mean that the page in your browser is refreshing, or just the component? If it's the former case, I don't know how to help you. However, I may have an answer for the latter.

Whenever there's a state change in a React component, the component will re-render . It looks like you're creating a new instance (const AsyncMap =...) of the GoogleMap component on every render. Try storing AsyncMap before the render function, as per this post (react-google-maps is refreshing the map when onclick is performed).


If that doesn't work, it may be something that the package does independent of React (I'm not familiar with the package that you're using.) In that case, try adding this before your render function.

shouldComponentUpdate(nextProps, nextState) {
  // Update in all cases EXCEPT when markerPosition changes
        if (nextState.markerPosition !==  this.state.markerPosition ) {
            return false;
        }
        return true;
      }
}