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

onPlaceSelected callback is not reactive

Open MoritzKn opened this issue 3 years ago • 3 comments

If the <ReactGoogleAutocomplete> component rerenders, the onPlaceSelected is not updated reaxtively. This is important in many cases where you have a callback that relies on some state.

This is because the event listener: https://github.com/ErrorPro/react-google-autocomplete/blob/214a7c3d4740d2bfcf85264717b0e0da5bd0b333/src/usePlacesWidget.js#L72-L75

...is only updated in the "init" useEffect that has no dependencies:

https://github.com/ErrorPro/react-google-autocomplete/blob/214a7c3d4740d2bfcf85264717b0e0da5bd0b333/src/usePlacesWidget.js#L93-L95

Reproduction Example

function MyComponent() {
  const [someState, setSomeState] = useState(false);

  // "toggleState" assinged a new function everytime "someState" changes
  const toggleState = useCallback(() => {
    const newState = !someState;
    setSomeState(newState);
    console.log("new state is", newState);
  }, [someState]);

  // "onPlaceSelected" is assinged a new function everytime "toggleState" changes
  const onPlaceSelected = useCallback(
    (place) => {
      console.log(place);
      toggleState();
    },
    [toggleState]
  );

  return (
    <ReactGoogleAutocomplete
      apiKey={YOUR_GOOGLE_MAPS_API_KEY}
      // onPlaceSelected is not updated in subsequent renders
      onPlaceSelected={onPlaceSelected}
    />
  );
}

toggleState() will always set someState to true.

Workaround

Using useEffect and useRef you can workaround this:

function MyComponent() {
  const [someState, setSomeState] = useState(false);
  const onPlaceSelected = useRef(() => undefined);

  // "toggleState" assinged a new function everytime "someState" changes
  const toggleState = useCallback(() => {
    const newState = !someState;
    setSomeState(newState);
    console.log("new state is", newState);
  }, [someState]);

  // "onPlaceSelected" is assinged a new function everytime "toggleState" changes
  useEffect(() => {
    onPlaceSelected.current = (place) => {
      console.log(place);
      toggleState();
    };
  }, [toggleState]);

  return (
    <Autocomplete
      apiKey={YOUR_GOOGLE_MAPS_API_KEY}
      // onPlaceSelected is not updated in subsequent renders
      onPlaceSelected={(...args) => onPlaceSelected.current(args)}
    />
  );
}

MoritzKn avatar Jul 22 '22 10:07 MoritzKn

Good one, will check it as well

ErrorPro avatar Aug 18 '22 10:08 ErrorPro

Thank you for looking into all the issues!

MoritzKn avatar Aug 18 '22 12:08 MoritzKn

hi, is there a fix for this?

zocoi avatar Mar 02 '24 00:03 zocoi