react-native-google-places-autocomplete icon indicating copy to clipboard operation
react-native-google-places-autocomplete copied to clipboard

Bug Report

Open saransh1997 opened this issue 9 months ago • 22 comments

Describe the bug

in GooglePlacesAutoComplete.js , cannot read property 'filter' of undefined is coming, below is the code snippet where the issue lies please check and update the code accordingly. res = [ ...props.predefinedPlaces.filter( (place) => place?.description.length, ), ];

  • Library Version: [ 2.5.7]

  • React Native Version: [0.78.0]

  • [ ] iOS

  • [x] Android

  • [ ] Web

saransh1997 avatar Mar 02 '25 18:03 saransh1997

getting the same issue

Praveenesarla avatar Mar 06 '25 07:03 Praveenesarla

same issue is happening for me also with react native 0.78.0

charithnidarsha avatar Mar 06 '25 17:03 charithnidarsha

Yes same issue here. React Native version is 0.78.0

Rafayyy000 avatar Mar 06 '25 17:03 Rafayyy000

setting predefinedPlaces={[]} prop to an emty array resolves this issue for me

ivansinuyk avatar Mar 06 '25 21:03 ivansinuyk

same issue

Nader-CS avatar Mar 06 '25 22:03 Nader-CS

setting predefinedPlaces={[]} prop to an emty array resolves this issue for me

fixes the crash but when i type anything i get Error: Exception in HostFunction: Expected argument 7 of method "sendRequest" to be a number, but got undefined

Nader-CS avatar Mar 06 '25 22:03 Nader-CS

I got this error when I applied that fix "TypeError: Cannot read property 'onFocus' of undefined". I then set onFocus via textinputprops and then set container styles. It is working now

setting predefinedPlaces={[]} prop to an emty array resolves this issue for me

Rafayyy000 avatar Mar 07 '25 13:03 Rafayyy000

setting predefinedPlaces={[]} prop to an emty array resolves this issue for me

fixes the crash but when i type anything i get Error: Exception in HostFunction: Expected argument 7 of method "sendRequest" to be a number, but got undefined

Same. Any ideas?

laurenwestonn avatar Mar 07 '25 16:03 laurenwestonn

setting predefinedPlaces={[]} prop to an emty array resolves this issue for me

yes this fixed the issue. But query suggestions are not working.

charithnidarsha avatar Mar 07 '25 17:03 charithnidarsha

Also, I have this problem on React Native 0.78.0. Is there any solution?

stoska avatar Mar 08 '25 19:03 stoska

any updates ?

Nader-CS avatar Mar 09 '25 08:03 Nader-CS

Hi guy, I was able to resolve the issue. You would have replace the GooglePlacesAutocomplete.js source code with the code below.

After resolving the undefined error. An XMLHTTP error popped up due to undefined props in the timeout and onTimeout

/* eslint-disable react-native/no-inline-styles */
import debounce from 'lodash.debounce';
import PropTypes from 'prop-types';
import Qs from 'qs';
import React, {
  forwardRef,
  useMemo,
  useEffect,
  useImperativeHandle,
  useRef,
  useState,
  useCallback,
} from 'react';
import {
  ActivityIndicator,
  Image,
  Keyboard,
  Platform,
  Pressable,
  StyleSheet,
  Text,
  TextInput,
  View,
} from 'react-native';

const defaultStyles = {
  container: {
    flex: 1,
  },
  textInputContainer: {
    flexDirection: 'row',
  },
  textInput: {
    backgroundColor: '#FFFFFF',
    height: 44,
    borderRadius: 5,
    paddingVertical: 5,
    paddingHorizontal: 10,
    fontSize: 15,
    flex: 1,
    marginBottom: 5,
  },
  listView: {},
  row: {
    backgroundColor: '#FFFFFF',
    padding: 13,
    minHeight: 44,
    flexDirection: 'row',
  },
  loader: {
    flexDirection: 'row',
    justifyContent: 'flex-end',
    height: 20,
  },
  description: {},
  separator: {
    height: StyleSheet.hairlineWidth,
    backgroundColor: '#c8c7cc',
  },
  poweredContainer: {
    justifyContent: 'flex-end',
    alignItems: 'center',
    borderBottomRightRadius: 5,
    borderBottomLeftRadius: 5,
    borderColor: '#c8c7cc',
    borderTopWidth: 0.5,
  },
  powered: {},
};

export const GooglePlacesAutocomplete = forwardRef((props, ref) => {
  let _results = [];
  let _requests = [];

  const onTimeoutFunc = () => console.warn('google places autocomplete: request timeout');
  const onTimeout = props.onTimeout ?? onTimeoutFunc;
  const timeout = props.timeout ?? 20000;

  const hasNavigator = () => {
    if (navigator?.geolocation) {
      return true;
    } else {
      console.warn(
        'If you are using React Native v0.60.0+ you must follow these instructions to enable currentLocation: https://git.io/Jf4AR',
      );
      return false;
    }
  };

  const buildRowsFromResults = useCallback(
    (results, text) => {
      let res = [];
      const shouldDisplayPredefinedPlaces = text
        ? results.length === 0 && text.length === 0
        : results.length === 0;

      const isPredefinedPlaceVisible =
        props.predefinedPlacesAlwaysVisible &&
        props.predefinedPlacesAlwaysVisible === true
          ? true
          : false;

      if (shouldDisplayPredefinedPlaces || isPredefinedPlaceVisible) {
        res = props.predefinedPlaces
          ? [
              ...props.predefinedPlaces.filter(
                place => place?.description.length,
              ),
            ]
          : [];

        if (props.currentLocation === true && hasNavigator()) {
          res.unshift({
            description: props.currentLocationLabel,
            isCurrentLocation: true,
          });
        }
      }

      res = res.map(place => ({
        ...place,
        isPredefinedPlace: true,
      }));

      return [...res, ...results];
    },
    [
      props.currentLocation,
      props.currentLocationLabel,
      props.predefinedPlaces,
      props.predefinedPlacesAlwaysVisible,
    ],
  );

  const getRequestUrl = useCallback(requestUrl => {
    if (requestUrl) {
      if (requestUrl.useOnPlatform === 'all') {
        return requestUrl.url;
      }
      if (requestUrl.useOnPlatform === 'web') {
        return Platform.select({
          web: requestUrl.url,
          default: 'https://maps.googleapis.com/maps/api',
        });
      }
    } else {
      return 'https://maps.googleapis.com/maps/api';
    }
  }, []);

  const getRequestHeaders = requestUrl => {
    return requestUrl?.headers || {};
  };

  const setRequestHeaders = (request, headers) => {
    Object.keys(headers).map(headerKey =>
      request.setRequestHeader(headerKey, headers[headerKey]),
    );
  };

  const [stateText, setStateText] = useState('');
  const [dataSource, setDataSource] = useState(buildRowsFromResults([]));
  const [listViewDisplayed, setListViewDisplayed] = useState(
    props.listViewDisplayed === 'auto' ? false : props.listViewDisplayed,
  );
  const [url, setUrl] = useState(getRequestUrl(props.requestUrl));
  const [listLoaderDisplayed, setListLoaderDisplayed] = useState(false);

  const inputRef = useRef();

  useEffect(() => {
    setUrl(getRequestUrl(props.requestUrl));
  }, [getRequestUrl, props.requestUrl]);

  useEffect(() => {
    // This will load the search results after the query object ref gets changed
    _handleChangeText(stateText);
    return () => {
      _abortRequests();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [props.query]);

  useEffect(() => {
    // Update dataSource if props.predefinedPlaces changed
    setDataSource(buildRowsFromResults([]));
  }, [buildRowsFromResults, props.predefinedPlaces]);

  useImperativeHandle(ref, () => ({
    setAddressText: address => {
      setStateText(address);
    },
    getAddressText: () => stateText,
    blur: () => inputRef.current.blur(),
    focus: () => inputRef.current.focus(),
    isFocused: () => inputRef.current.isFocused(),
    clear: () => inputRef.current.clear(),
    getCurrentLocation,
  }));

  const requestShouldUseWithCredentials = () =>
    url === 'https://maps.googleapis.com/maps/api';

  const _abortRequests = () => {
    _requests.map(i => {
      i.onreadystatechange = null;
      i.abort();
    });
    _requests = [];
  };

  const supportedPlatform = () => {
    if (Platform.OS === 'web' && !props.requestUrl) {
      console.warn(
        'This library cannot be used for the web unless you specify the requestUrl prop. See https://git.io/JflFv for more for details.',
      );
      return false;
    } else {
      return true;
    }
  };

  const getCurrentLocation = () => {
    let options = {
      enableHighAccuracy: false,
      timeout: 20000,
      maximumAge: 1000,
    };

    if (props.enableHighAccuracyLocation && Platform.OS === 'android') {
      options = {
        enableHighAccuracy: true,
        timeout: 20000,
      };
    }
    const getCurrentPosition =
      navigator.geolocation.getCurrentPosition ||
      navigator.geolocation.default.getCurrentPosition;

    getCurrentPosition &&
      getCurrentPosition(
        position => {
          if (props.nearbyPlacesAPI === 'None') {
            let currentLocation = {
              description: props.currentLocationLabel,
              geometry: {
                location: {
                  lat: position.coords.latitude,
                  lng: position.coords.longitude,
                },
              },
            };

            _disableRowLoaders();
            props.onPress(currentLocation, currentLocation);
          } else {
            _requestNearby(position.coords.latitude, position.coords.longitude);
          }
        },
        error => {
          _disableRowLoaders();
          console.error(error.message);
        },
        options,
      );
  };

  const _onPress = rowData => {
    if (rowData.isPredefinedPlace !== true && props.fetchDetails === true) {
      if (rowData.isLoading === true) {
        // already requesting
        return;
      }

      Keyboard.dismiss();

      _abortRequests();

      // display loader
      _enableRowLoader(rowData);

      // fetch details
      const request = new XMLHttpRequest();
      _requests.push(request);
      const onTimeout = () => console.warn('google places autocomplete: request timeout');
      request.timeout = props.timeout ?? 20000;
      request.ontimeout = props.onTimeout ?? onTimeout

      request.onreadystatechange = () => {
        if (request.readyState !== 4) return;

        if (request.status === 200) {
          const responseJSON = JSON.parse(request.responseText);

          if (responseJSON.status === 'OK') {
            // if (_isMounted === true) {
            const details = responseJSON.result;
            _disableRowLoaders();
            _onBlur();

            setStateText(_renderDescription(rowData));

            delete rowData.isLoading;
            props.onPress(rowData, details);
            // }
          } else {
            _disableRowLoaders();

            if (props.autoFillOnNotFound) {
              setStateText(_renderDescription(rowData));
              delete rowData.isLoading;
            }

            if (!props.onNotFound) {
              console.warn(
                'google places autocomplete: ' + responseJSON.status,
              );
            } else {
              props.onNotFound(responseJSON);
            }
          }
        } else {
          _disableRowLoaders();

          if (!props.onFail) {
            console.warn(
              'google places autocomplete: request could not be completed or has been aborted',
            );
          } else {
            props.onFail('request could not be completed or has been aborted');
          }
        }
      };

      request.open(
        'GET',
        `${url}/place/details/json?` +
          Qs.stringify({
            key: props.query.key,
            placeid: rowData.place_id,
            language: props.query.language,
            ...props.GooglePlacesDetailsQuery,
          }),
      );

      request.withCredentials = requestShouldUseWithCredentials();
      setRequestHeaders(request, getRequestHeaders(props.requestUrl));

      request.send();
    } else if (rowData.isCurrentLocation === true) {
      // display loader
      _enableRowLoader(rowData);

      setStateText(_renderDescription(rowData));

      delete rowData.isLoading;
      getCurrentLocation();
    } else {
      setStateText(_renderDescription(rowData));

      _onBlur();
      delete rowData.isLoading;
      let predefinedPlace = _getPredefinedPlace(rowData);

      // sending predefinedPlace as details for predefined places
      props.onPress(predefinedPlace, predefinedPlace);
    }
  };

  const _enableRowLoader = rowData => {
    let rows = buildRowsFromResults(_results);
    for (let i = 0; i < rows.length; i++) {
      if (
        rows[i].place_id === rowData.place_id ||
        (rows[i].isCurrentLocation === true &&
          rowData.isCurrentLocation === true)
      ) {
        rows[i].isLoading = true;
        setDataSource(rows);
        break;
      }
    }
  };

  const _disableRowLoaders = () => {
    // if (_isMounted === true) {
    for (let i = 0; i < _results.length; i++) {
      if (_results[i].isLoading === true) {
        _results[i].isLoading = false;
      }
    }

    setDataSource(buildRowsFromResults(_results));
    // }
  };

  const _getPredefinedPlace = rowData => {
    if (rowData.isPredefinedPlace !== true) {
      return rowData;
    }

    for (let i = 0; i < props.predefinedPlaces.length; i++) {
      if (props.predefinedPlaces[i].description === rowData.description) {
        return props.predefinedPlaces[i];
      }
    }

    return rowData;
  };

  const _filterResultsByTypes = (unfilteredResults, types) => {
    if (types.length === 0) return unfilteredResults;

    const results = [];
    for (let i = 0; i < unfilteredResults.length; i++) {
      let found = false;

      for (let j = 0; j < types.length; j++) {
        if (unfilteredResults[i].types.indexOf(types[j]) !== -1) {
          found = true;
          break;
        }
      }

      if (found === true) {
        results.push(unfilteredResults[i]);
      }
    }
    return results;
  };

  const _requestNearby = (latitude, longitude) => {
    _abortRequests();

    if (
      latitude !== undefined &&
      longitude !== undefined &&
      latitude !== null &&
      longitude !== null
    ) {
      const request = new XMLHttpRequest();
      _requests.push(request);
      request.timeout = props.timeout;
      request.ontimeout = props.onTimeout;
      request.onreadystatechange = () => {
        if (request.readyState !== 4) {
          setListLoaderDisplayed(true);
          return;
        }

        setListLoaderDisplayed(false);
        if (request.status === 200) {
          const responseJSON = JSON.parse(request.responseText);

          _disableRowLoaders();

          if (typeof responseJSON.results !== 'undefined') {
            // if (_isMounted === true) {
            var results = [];
            if (props.nearbyPlacesAPI === 'GoogleReverseGeocoding') {
              results = _filterResultsByTypes(
                responseJSON.results,
                props.filterReverseGeocodingByTypes,
              );
            } else {
              results = responseJSON.results;
            }

            setDataSource(buildRowsFromResults(results));
            // }
          }
          if (typeof responseJSON.error_message !== 'undefined') {
            if (!props.onFail)
              console.warn(
                'google places autocomplete: ' + responseJSON.error_message,
              );
            else {
              props.onFail(responseJSON.error_message);
            }
          }
        } else {
          // console.warn("google places autocomplete: request could not be completed or has been aborted");
        }
      };

      let requestUrl = '';
      if (props.nearbyPlacesAPI === 'GoogleReverseGeocoding') {
        // your key must be allowed to use Google Maps Geocoding API
        requestUrl =
          `${url}/geocode/json?` +
          Qs.stringify({
            latlng: latitude + ',' + longitude,
            key: props.query.key,
            ...props.GoogleReverseGeocodingQuery,
          });
      } else {
        requestUrl =
          `${url}/place/nearbysearch/json?` +
          Qs.stringify({
            location: latitude + ',' + longitude,
            key: props.query.key,
            ...props.GooglePlacesSearchQuery,
          });
      }

      request.open('GET', requestUrl);

      request.withCredentials = requestShouldUseWithCredentials();
      setRequestHeaders(request, getRequestHeaders(props.requestUrl));

      request.send();
    } else {
      _results = [];
      setDataSource(buildRowsFromResults([]));
    }
  };

  const _request = text => {
    _abortRequests();
    if (!url) {
      return;
    }

    const isSearchTerm = text
      ? props.minLength && text.length >= props.minLength
        ? true
        : text.length >= 0
      : false

    console.log(isSearchTerm)

    if (supportedPlatform() && isSearchTerm ) {
      const request = new XMLHttpRequest();
      _requests.push(request);
  
      request.timeout = timeout ;
      request.ontimeout = onTimeout;
      request.onreadystatechange = () => {
        if (request.readyState !== 4) {
          setListLoaderDisplayed(true);
          return;
        }

        setListLoaderDisplayed(false);
        if (request.status === 200) {
          const responseJSON = JSON.parse(request.responseText);
          if (typeof responseJSON.predictions !== 'undefined') {
            // if (_isMounted === true) {
            const results =
              props.nearbyPlacesAPI === 'GoogleReverseGeocoding'
                ? _filterResultsByTypes(
                    responseJSON.predictions,
                    props.filterReverseGeocodingByTypes,
                  )
                : responseJSON.predictions;

            _results = results;
            setDataSource(buildRowsFromResults(results, text));
            // }
          }
          if (typeof responseJSON.error_message !== 'undefined') {
            if (!props.onFail)
              console.warn(
                'google places autocomplete: ' + responseJSON.error_message,
              );
            else {
              props.onFail(responseJSON.error_message);
            }
          }
        } else {
          // console.warn("google places autocomplete: request could not be completed or has been aborted");
        }
      };

      if (props.preProcess) {
        setStateText(props.preProcess(text));
      }

      request.open(
        'GET',
        `${url}/place/autocomplete/json?input=` +
          encodeURIComponent(text) +
          '&' +
          Qs.stringify(props.query),
      );

      request.withCredentials = requestShouldUseWithCredentials();
      setRequestHeaders(request, getRequestHeaders(props.requestUrl));

      request.send();
    } else {
      _results = [];
      setDataSource(buildRowsFromResults([]));
    }
  };

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const debounceData = useMemo(
    () => debounce(_request, props.debounce),
    [props.query, url],
  );

  const _onChangeText = text => {
    setStateText(text);
    debounceData(text);
  };

  const _handleChangeText = text => {
    _onChangeText(text);

    const onChangeText = props?.textInputProps?.onChangeText;

    if (onChangeText) {
      onChangeText(text);
    }
  };

  const _getRowLoader = () => {
    return <ActivityIndicator animating={true} size="small" />;
  };

  const _renderRowData = (rowData, index) => {
    if (props.renderRow) {
      return props.renderRow(rowData, index);
    }

    return (
      <Text
        style={[
          props.suppressDefaultStyles ? {} : defaultStyles.description,
          props.styles.description,
          rowData.isPredefinedPlace
            ? props.styles.predefinedPlacesDescription
            : {},
        ]}
        numberOfLines={props.numberOfLines}>
        {_renderDescription(rowData)}
      </Text>
    );
  };

  const _renderDescription = rowData => {
    if (props.renderDescription) {
      return props.renderDescription(rowData);
    }

    return rowData.description || rowData.formatted_address || rowData.name;
  };

  const _renderLoader = rowData => {
    if (rowData.isLoading === true) {
      return (
        <View
          style={[
            props.suppressDefaultStyles ? {} : defaultStyles.loader,
            props.styles.loader,
          ]}>
          {_getRowLoader()}
        </View>
      );
    }

    return null;
  };

  const _renderRow = useCallback(
    (rowData = {}, index) => {
      return (
        <Pressable
          key={Math.random().toString(36)}
          style={({ hovered, pressed }) => [
            props.isRowScrollable ? { minWidth: '100%' } : { width: '100%' },
            {
              backgroundColor: pressed
                ? props.listUnderlayColor
                : hovered
                ? props.listHoverColor
                : undefined,
            },
          ]}
          onPress={() => _onPress(rowData)}
          onBlur={_onBlur}>
          <View
            style={[
              props.suppressDefaultStyles ? {} : defaultStyles.row,
              props.styles.row,
              rowData.isPredefinedPlace ? props.styles.specialItemRow : {},
            ]}>
            {_renderLoader(rowData)}
            {_renderRowData(rowData, index)}
          </View>
        </Pressable>
      );
    },
    [props],
  );

  const _renderSeparator = (sectionID, rowID) => {
    if (rowID === dataSource.length - 1) {
      return null;
    }

    return (
      <View
        key={`${sectionID}-${rowID}`}
        style={[
          props.suppressDefaultStyles ? {} : defaultStyles.separator,
          props.styles.separator,
        ]}
      />
    );
  };

  const isNewFocusInAutocompleteResultList = ({
    relatedTarget,
    currentTarget,
  }) => {
    if (!relatedTarget) return false;

    var node = relatedTarget.parentNode;

    while (node) {
      if (node.id === 'result-list-id') return true;
      node = node.parentNode;
    }

    return false;
  };

  const _onBlur = e => {
    if (e && isNewFocusInAutocompleteResultList(e)) return;

    if (!props.keepResultsAfterBlur) {
      setListViewDisplayed(false);
    }
    inputRef?.current?.blur();
  };

  const _onFocus = () => setListViewDisplayed(true);

  const _renderPoweredLogo = () => {
    if (!_shouldShowPoweredLogo()) {
      return null;
    }

    return (
      <View
        style={[
          props.suppressDefaultStyles ? {} : defaultStyles.row,
          defaultStyles.poweredContainer,
          props.styles.poweredContainer,
        ]}>
        <Image
          style={[
            props.suppressDefaultStyles ? {} : defaultStyles.powered,
            props.styles.powered,
          ]}
          resizeMode="contain"
          source={require('./images/powered_by_google_on_white.png')}
        />
      </View>
    );
  };

  const _shouldShowPoweredLogo = () => {
    if (!props.enablePoweredByContainer || dataSource.length === 0) {
      return false;
    }

    for (let i = 0; i < dataSource.length; i++) {
      let row = dataSource[i];

      if (
        !row.hasOwnProperty('isCurrentLocation') &&
        !row.hasOwnProperty('isPredefinedPlace')
      ) {
        return true;
      }
    }

    return false;
  };

  const _renderLeftButton = () => {
    if (props.renderLeftButton) {
      return props.renderLeftButton();
    }
  };

  const _renderRightButton = () => {
    if (props.renderRightButton) {
      return props.renderRightButton();
    }
  };

  const _getFlatList = useCallback(() => {
    if (stateText !== '' && listViewDisplayed === true) {
      return (
        <View style={{ zIndex: 0 }}>
          {[...dataSource].slice(0, 5).map((item, index) => {
            return _renderRow(item, index);
          })}
        </View>
      );
    }

    return null;
  }, [dataSource]);

  let {
    onFocus,
    onBlur,
    onChangeText,
    clearButtonMode,
    InputComp,
    ...userProps
  } = props.textInputProps;
  const TextInputComp = InputComp || TextInput;

  return (
    <View
      style={[
        props.suppressDefaultStyles ? {} : defaultStyles.container,
        props.styles.container,
      ]}
      pointerEvents="box-none">
      {!props.textInputHide && (
        <View
          style={[
            props.suppressDefaultStyles ? {} : defaultStyles.textInputContainer,
            props.styles.textInputContainer,
          ]}>
          {_renderLeftButton()}
          <TextInputComp
            ref={inputRef}
            style={[
              props.suppressDefaultStyles ? {} : defaultStyles.textInput,
              props.styles.textInput,
            ]}
            value={stateText}
            placeholder={props.placeholder}
            onFocus={
              onFocus
                ? e => {
                    _onFocus();
                    onFocus(e);
                  }
                : _onFocus
            }
            onBlur={
              onBlur
                ? e => {
                    _onBlur(e);
                    onBlur(e);
                  }
                : _onBlur
            }
            clearButtonMode={clearButtonMode || 'while-editing'}
            onChangeText={_handleChangeText}
            {...userProps}
          />
          {_renderRightButton()}
        </View>
      )}
      {props.inbetweenCompo}
      {_getFlatList()}
      {props.children}
    </View>
  );
});

GooglePlacesAutocomplete.propTypes = {
  autoFillOnNotFound: PropTypes.bool,
  currentLocation: PropTypes.bool,
  currentLocationLabel: PropTypes.string,
  debounce: PropTypes.number,
  disableScroll: PropTypes.bool,
  enableHighAccuracyLocation: PropTypes.bool,
  enablePoweredByContainer: PropTypes.bool,
  fetchDetails: PropTypes.bool,
  filterReverseGeocodingByTypes: PropTypes.array,
  GooglePlacesDetailsQuery: PropTypes.object,
  GooglePlacesSearchQuery: PropTypes.object,
  GoogleReverseGeocodingQuery: PropTypes.object,
  inbetweenCompo: PropTypes.object,
  isRowScrollable: PropTypes.bool,
  keyboardShouldPersistTaps: PropTypes.oneOf(['never', 'always', 'handled']),
  listEmptyComponent: PropTypes.element,
  listLoaderComponent: PropTypes.element,
  listHoverColor: PropTypes.string,
  listUnderlayColor: PropTypes.string,
  // Must write it this way: https://stackoverflow.com/a/54290946/7180620
  listViewDisplayed: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.oneOf(['auto']),
  ]),
  keepResultsAfterBlur: PropTypes.bool,
  minLength: PropTypes.number,
  nearbyPlacesAPI: PropTypes.string,
  numberOfLines: PropTypes.number,
  onFail: PropTypes.func,
  onNotFound: PropTypes.func,
  onPress: PropTypes.func,
  onTimeout: PropTypes.func,
  placeholder: PropTypes.string,
  predefinedPlaces: PropTypes.array,
  predefinedPlacesAlwaysVisible: PropTypes.bool,
  preProcess: PropTypes.func,
  query: PropTypes.object,
  renderDescription: PropTypes.func,
  renderHeaderComponent: PropTypes.func,
  renderLeftButton: PropTypes.func,
  renderRightButton: PropTypes.func,
  renderRow: PropTypes.func,
  requestUrl: PropTypes.shape({
    url: PropTypes.string,
    useOnPlatform: PropTypes.oneOf(['web', 'all']),
    headers: PropTypes.objectOf(PropTypes.string),
  }),
  styles: PropTypes.object,
  suppressDefaultStyles: PropTypes.bool,
  textInputHide: PropTypes.bool,
  textInputProps: PropTypes.object,
  timeout: PropTypes.number,
};

GooglePlacesAutocomplete.defaultProps = {
  autoFillOnNotFound: false,
  currentLocation: false,
  currentLocationLabel: 'Current location',
  debounce: 0,
  disableScroll: false,
  enableHighAccuracyLocation: true,
  enablePoweredByContainer: true,
  fetchDetails: false,
  filterReverseGeocodingByTypes: [],
  GooglePlacesDetailsQuery: {},
  GooglePlacesSearchQuery: {
    rankby: 'distance',
    type: 'restaurant',
  },
  GoogleReverseGeocodingQuery: {},
  isRowScrollable: true,
  keyboardShouldPersistTaps: 'always',
  listHoverColor: '#ececec',
  listUnderlayColor: '#c8c7cc',
  listViewDisplayed: 'auto',
  keepResultsAfterBlur: false,
  minLength: 0,
  nearbyPlacesAPI: 'GooglePlacesSearch',
  numberOfLines: 1,
  onFail: () => {},
  onNotFound: () => {},
  onPress: () => {},
  onTimeout: () => console.warn('google places autocomplete: request timeout'),
  placeholder: '',
  predefinedPlaces: [],
  predefinedPlacesAlwaysVisible: false,
  query: {
    key: 'missing api key',
    language: 'en',
    types: 'geocode',
  },
  styles: {},
  suppressDefaultStyles: false,
  textInputHide: false,
  textInputProps: {},
  timeout: 20000,
};

GooglePlacesAutocomplete.displayName = 'GooglePlacesAutocomplete';

export default { GooglePlacesAutocomplete };

mannie411 avatar Mar 09 '25 17:03 mannie411

any updates ?

check my comment above

mannie411 avatar Mar 09 '25 17:03 mannie411

@mannie411 That worked great. But that "powered_by_google_on_white.png" uri didn't work for me. i changed to './images/powered_by_google_on_white.png'

jrubansn avatar Mar 10 '25 05:03 jrubansn

@mannie411 Thanks I'm no longer seeing the issues! I just have a question on your solution;

how come you removed the ScrollView and the FlatList, and limited the results to 5? I worry this is going to change how the autocomplete component looks and works.

Was this change necessary to fix the bug or are your other changes (handling null or undefined props, and the timeout) enough to fix this? I'd rather not change it if I can avoid it

laurenwestonn avatar Mar 10 '25 09:03 laurenwestonn

@laurenwestonn since i upgrade to react-native v0.78 the flatlist component was causing the entire app to crash and i didn't really have time to start debugging that so opted to use the scrollview.

you can referrence this onPress issues

mannie411 avatar Mar 10 '25 10:03 mannie411

17 march i also faced this issue please update the code.

whoproami avatar Mar 17 '25 14:03 whoproami

I am getting this error after using your code @mannie411

Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.

jamieGardyn avatar Mar 18 '25 04:03 jamieGardyn

@mannie411 after implementing your code i get Cannot read property 'onFocus' of undefined

MohitNarkhede avatar Mar 18 '25 10:03 MohitNarkhede

issue ++

WebtualUtsav avatar Mar 19 '25 09:03 WebtualUtsav

@mannie411 Modify let { onFocus, onBlur, onChangeText, clearButtonMode, InputComp, ...userProps } = props.textInputProps;

to

let { onFocus = () => {}, onBlur = () => {}, onChangeText = () => {}, clearButtonMode, InputComp, ...userProps } = props.textInputProps || {};

It worked for me. Not sure if it is recommended for production but useful for debugging.

mshuheb avatar Mar 21 '25 16:03 mshuheb

I encounter this bug as well after upgrading my app to RN 0.78. I've created a much faster and leaner (less than 300 lines of code) google places component. it is easier to style and has more features, it uses the New Places API, give it a try: https://github.com/amitpdev/react-native-google-places-textinput https://www.npmjs.com/package/react-native-google-places-textinput

amitpdev avatar Mar 26 '25 09:03 amitpdev

Added predefinedPlaces={[]}. This fixes the main "Cannot read property 'filter' of undefined" error by providing an empty array instead of undefined.

hafisFreshi avatar May 03 '25 09:05 hafisFreshi

This worked for me thanks

cermuel avatar May 05 '25 23:05 cermuel

I encounter this bug as well after upgrading my app to RN 0.78. I've created a much faster and leaner (less than 300 lines of code) google places component. it is easier to style and has more features, it uses the New Places API, give it a try: https://github.com/amitpdev/react-native-google-places-textinput https://www.npmjs.com/package/react-native-google-places-textinput

Do you have fetch details implemented? So I can get latitude and longitude of the place

nathanchann avatar May 11 '25 14:05 nathanchann

Adding the predefinedPlaces={[]} will only stop the error but it brokes the auto complete suggestion anyone with valid solution here !

dhairyasenjaliya avatar May 12 '25 10:05 dhairyasenjaliya

PR #970 worked for me including the completion suggestions. I pinned my package to the specific repo/branch/commit:

"react-native-google-places-autocomplete": "git+https://github.com/kostyngricuk/react-native-google-places-autocomplete#37cfe5ad87d12b701706bc58f76a5d3a0955e60e",

@amitpdev thanks for sharing your other lib, looks clean and looking to migrate over.

basememara avatar May 14 '25 10:05 basememara

setting predefinedPlaces={[]} prop to an emty array resolves this issue for me

fixes the crash but when i type anything i get Error: Exception in HostFunction: Expected argument 7 of method "sendRequest" to be a number, but got undefined

die you get any solution on this as i'm facing it from 3 datas

pankajsharma832 avatar Jun 20 '25 07:06 pankajsharma832

Try using all the props with default value. It worked for me

KrushnaVidyutva avatar Jun 20 '25 12:06 KrushnaVidyutva

add this to as component attribut: predefinedPlaces={[]}

peterchijioke avatar Jul 01 '25 09:07 peterchijioke