ErrorPro / react-google-autocomplete

React components for google places API.
MIT License
473 stars 114 forks source link

onPlaceSelected callback is not reactive #168

Open MoritzKn opened 2 years ago

MoritzKn commented 2 years ago

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)}
    />
  );
}
ErrorPro commented 2 years ago

Good one, will check it as well

MoritzKn commented 2 years ago

Thank you for looking into all the issues!

zocoi commented 6 months ago

hi, is there a fix for this?