Info:
This is release candidate documentation for Stream Chat React Native SDK v10. For the latest stable version, see the latest version (v9).

Location hooks

These hooks give a component the live location manager and follow the location shared in one message. They're exported from stream-chat-react-native and stream-chat-expo, with LiveLocationManagerProvider, the component that creates the manager.

Location sharing, and why live locations need a manager, is explained on Location sharing. The location being shared from the composer is in locationComposer.state: see Composer hooks.

Each entry gives the signature, then:

  • Returns: what the hook gives back.
  • Reads: the store or the context it reads.
  • Re-renders: what makes the component render again.
  • Needs: where the component must sit, and what the hook does outside that place.

LiveLocationManagerProvider

Creates the manager that sends the user's position to every live location they're sharing.

LiveLocationManagerProvider(props: PropsWithChildren<{
  watchLocation: WatchLocation;
  getDeviceId?: () => string;
}>)

The props:

  • watchLocation: (handler) => unsubscribe. It starts watching the device's position and calls handler with each { latitude, longitude }. The manager calls it while at least one live share is active, and calls the returned function when none is left.
  • getDeviceId: the ID of the device the shares come from. By default react-native-<platform>-<user ID>, such as react-native-ios-cleo.

It creates a LiveLocationManager for the client of Chat, starts it with LiveLocationManager.init(), and disposes of it when it unmounts. A new watchLocation or getDeviceId creates a new manager, so declare them at module scope. It needs Chat above it.

Render it once, around the part of the app that stays mounted while the user is signed in. Two managers would both send each position. The SampleApp wraps its navigator in it, with a watchLocation built on @react-native-community/geolocation:

import type { PropsWithChildren } from "react";
import Geolocation from "@react-native-community/geolocation";
import type { WatchLocation } from "stream-chat";
import { LiveLocationManagerProvider } from "stream-chat-react-native";

const watchLocation: WatchLocation = (handler) => {
  const watchId = Geolocation.watchPosition(
    ({ coords }) =>
      handler({ latitude: coords.latitude, longitude: coords.longitude }),
    (error) => console.warn("Watching the location failed", error),
    { enableHighAccuracy: true },
  );
  return () => Geolocation.clearWatch(watchId);
};

export const LiveLocationRoot = ({ children }: PropsWithChildren) => (
  <LiveLocationManagerProvider watchLocation={watchLocation}>
    {children}
  </LiveLocationManagerProvider>
);

The app asks for location permission itself, before the user shares a live location.

useLiveLocationManagerContext

Returns the live location manager.

useLiveLocationManagerContext(): { liveLocationManager: LiveLocationManager | null }
  • Returns: { liveLocationManager }, the manager LiveLocationManagerProvider created. null outside the provider.
  • Reads: the context of LiveLocationManagerProvider. No store. The manager keeps its own state in liveLocationManager.state.
  • Re-renders: when the provider renders, since it passes a new value each time.
  • Needs: nothing. Never throws.

The manager's state holds two fields:

  • ready: whether the manager has loaded the user's active live locations.
  • messages: a Map from message ID to each live location the user is sharing, with its end_at.
import { Text } from "react-native";
import type { LiveLocationManagerState } from "stream-chat";
import {
  useLiveLocationManagerContext,
  useStateStore,
} from "stream-chat-react-native";

const sharesSelector = ({ messages }: LiveLocationManagerState) => ({
  messages,
});

export const LiveLocationStatus = () => {
  const { liveLocationManager } = useLiveLocationManagerContext();
  const { messages } =
    useStateStore(liveLocationManager?.state, sharesSelector) ?? {};

  if (!messages?.size) return null;
  return <Text>Sharing your live location in {messages.size} messages</Text>;
};

useHandleLiveLocationEvents

Follows the location shared in one message.

useHandleLiveLocationEvents(params: {
  channel: Channel;
  messageId: string;
  onLocationUpdate?: (location: SharedLocationResponse) => void;
}): {
  isLiveLocationStopped: boolean | null;
  locationResponse: SharedLocationResponse | undefined;
}
  • Returns:
    • locationResponse: the message's shared_location from the latest message.updated event. undefined until the first one arrives, so start from message.shared_location and prefer this value once it's set.
    • isLiveLocationStopped: true once an update arrives whose end_at has passed. null before that. A live location that expires without a further update stays null, so compare end_at with the current time as well.
  • Reads: the message.updated events of the channel for messageId. It calls onLocationUpdate with each new location.
  • Re-renders: when an update for the message arrives.
  • Needs: Chat. Throws outside it.

A new onLocationUpdate function subscribes again, so define it with useCallback. The SDK's components don't call this hook. The SampleApp's map screen uses it to move the marker of a live location.

A location shown in a message is message.shared_location, and changes with the message's message.updated event.