# Contexts and Hooks

The SDK follows a framework-agnostic reactive state management pattern. If you're creating a React application you'll probably want to access the reactive state using hooks or passing them through context providers; the SDK comes with various quality-of-life state and utility hooks for you to use within your application.

## State Hooks

The state hooks are essentially wrappers around reactive state properties, making their usage trivial. Namely, these can be divided into the following groups:

- Client state hooks
  - `useClientConnectedUser()` - get the user that has been connected through `client.connectUser` and `null` otherwise
  - `useWsConnectionState()` - get the websocket connection state for the currently connected user
- Feed state hooks
  - `useActivityComments(...)` - get reactive comment list
  - `useOwnCapabilities(feed)` - get permissions for currently connected user for that particular feed
  - `useFollowers(feed)` - get a reactive list of followers of the `feed` in question (who/what follows that `feed`)
  - `useFollowing(feed)` - get a reactive list of follows of the `feed` in question (who/what that `feed` follows)
  - `useFeedActivities(feed)` - get a reactive list of activities for a feed and its pagination options
  - `useMembers(feed)` - get a reactive list of feed members with pagination and loadNextPage
  - `useOwnFollows(feed)` - get a reactive list of `FollowResponse`s from your own feeds towards a given `feed`
  - `useOwnFollowings(feed)` - get a reactive list of `FollowResponse`s from the owner of a given `feed` towards you
  - `useAggregatedActivities(feed)` - get a reactive list of aggregated activities for a feed
  - `useNotificationStatus(feed)` - get a reactive set of properties of the `notification_status` of a feed (reads, seen activities etc.)
- Search state hooks
  - `useSearchQuery(searchController)` - get a reactive value of the current search query embedded within the `SearchController`
  - `useSearchSources(searchController)` - get a reactive list of the currently registered `SearchSource`s on the `SearchController`
  - `useSearchResult(searchSource)` - get a reactive list of results for a given search query and its pagination options

and finally `useStateStore` from which all of the previously mentioned state hooks are derived.

### useClientConnectedUser

In order to not have to wait for user connection to finish through `client.connectUser`, we've introduced a reactive property of the `client` state called `connectedUser`. The purpose of this hook is to return that specific reactive value that can then be used to determine how we render the UI and which parts are supposed to be placeholders until the user connects, rather than blocking the entire UI.

If connection has not finished yet, it will return `null`.

```tsx label="React"
import { useClientConnectedUser } from "@stream-io/feeds-react-sdk";

const FeedComponent = () => {
  const connectedUser = useClientConnectedUser();

  if (!connectedUser) {
    return /* Render some placeholder */;
  }

  return /* Render the actual component tree */;
};
```

>
> **Info:** Note that it is important that the `useClientConnectedUser` hook is used within the `StreamFeedsContext` or within the `StreamFeeds` wrapper. It will simply return `null` otherwise.
>

### useWsConnectionState

It is occasionally the case that due to unstable network conditions, complete loss of network or other circumstances the websocket connection is broken. This hook returns an object containing information about the current state of the websocket connection.

```tsx label="React"
import { useWsConnectionState } from "@stream-io/feeds-react-sdk";

const FeedComponent = () => {
  const { is_healthy: isHealthy } = useWsConnectionState();

  return (
    <>
      {isHealthy ? null : <ConnectionLostBanner />}
      <ActualContent />
    </>
  );
};
```

>
> **Info:** Note that it is important that the `useWsConnectionState` hook is used within the `StreamFeedsContext` or within the `StreamFeeds` wrapper. It will simply return `{ isHealthy: false }` otherwise.
>

### useActivityComments

Comments in feeds can nest multiple levels deep. To simplify working with the state structure and keep things fast, we've introduced the `useActivityComments` hook which provides the comments, pagination as well as some pagination utility methods.

The hook accepts the following:

- `feed` (optional) - a `Feed` instance used to load comments. If omitted, the hook uses the feed from the nearest `StreamFeedContext`. When viewing an activity outside a feed (e.g. an activity details page), omit `feed` and provide `activity` instead (or wrap with `StreamActivityWithStateUpdates` so `activity` comes from context).
- `activity` (optional) - an `ActivityResponse` or `ActivityWithStateUpdates` for which to show comments. If omitted, the hook uses the activity from the nearest `StreamActivityWithStateUpdatesContext` (when inside a `StreamActivityWithStateUpdates` wrapper). Use `ActivityWithStateUpdates` on activity details pages so comments can be loaded from the activity instance.
- `parentComment` (optional) - a `CommentResponse`; pass this when showing replies to a specific comment.

You must provide either a `feed` or an `activity` that supports loading comments, and either an `activity` or a `parentComment` (so the hook knows which entity's comments to load).

```tsx label="React"
import type {
  ActivityResponse,
  CommentResponse,
  Feed,
  ActivityWithStateUpdates,
} from "@stream-io/feeds-react-sdk";
import { useActivityComments } from "@stream-io/feeds-react-sdk";

// Inside a feed (e.g. feed view)
const Comments = ({
  activity,
  feed,
  parentComment,
}: {
  activity: ActivityResponse;
  parentComment?: CommentResponse;
  feed: Feed;
}) => {
  const {
    comments,
    comments_pagination,
    has_next_page,
    is_loading_next_page,
    loadNextPage,
  } = useActivityComments({ feed, activity, parentComment });

  return <div>...</div>;
};

// Activity details page: pass activity (or wrap with StreamActivityWithStateUpdates and omit it)
const ActivityDetailsComments = ({
  activity,
  parentComment,
}: {
  activity: ActivityWithStateUpdates;
  parentComment?: CommentResponse;
}) => {
  const {
    comments,
    comments_pagination,
    has_next_page,
    is_loading_next_page,
    loadNextPage,
  } = useActivityComments({ activity, parentComment });

  return <div>...</div>;
};
```

To load more comments (or initial pages) you can simply call the `loadNextPage` method.

>
> **Info:** The `feed` and `activity` arguments are optional. The hook resolves `feed` from the nearest `StreamFeedContext` (or `StreamFeed` wrapper) and `activity` from the nearest `StreamActivityWithStateUpdatesContext` (or `StreamActivityWithStateUpdates` wrapper). Pass them explicitly when not using those contexts, or when you need to override the context value.
>

### useFollowers

Having followers is a very popular usecase for activity feeds. To facilitate this, we've introduced a `useFollowers` hook that provides a list of followers, pagination as well as some pagination utility methods.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useFollowers } from "@stream-io/feeds-react-sdk";

const Followers = ({ feed }: { feed: Feed }) => {
  const {
    followers,
    follower_count,
    followers_pagination,
    is_loading_next_page,
    has_next_page,
    loadNextPage,
  } = useFollowers(feed);

  // or `useFollowers()` if the component is wrapped within a `StreamFeedContext`

  return <div>...</div>;
};
```

To load more followers (or initial pages) you can simply call the `loadNextPage` method. The hook will then pull the data from the state store based on the `feed`.

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useFollowers` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useFollowing

Similar to having followers, following users is also a very popular usecase for activity feeds. To facilitate this, we've introduced a `useFollowing` hook that provides a list of users we follow, pagination as well as some pagination utility methods.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useFollowing } from "@stream-io/feeds-react-sdk";

const Following = ({ feed }: { feed: Feed }) => {
  const {
    following,
    following_count,
    following_pagination,
    is_loading_next_page,
    has_next_page,
    loadNextPage,
  } = useFollowing(feed);

  // or `useFollowing()` if the component is wrapped within a `StreamFeedContext`

  return <div>...</div>;
};
```

To load more users you follow (or initial pages) you can simply call the `loadNextPage` method. The hook will then pull the data from the state store based on the `feed`.

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useFollowing` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useFeedActivities

Activities are the core atomic building blocks of an activity feed. For ease of use, we've introduced a `useFeedActivities` hook that provides a list of activities for a `feed`, pagination as well as some pagination utility methods.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useFeedActivities } from "@stream-io/feeds-react-sdk";

const Activities = ({ feed }: { feed: Feed }) => {
  const { activities, is_loading, has_next_page, loadNextPage } =
    useFeedActivities(feed) ?? {};

  // or `useFeedActivities()` if the component is wrapped within a `StreamFeedContext`

  return <div>...</div>;
};
```

To load more activities (or initial pages) you can simply call the `loadNextPage` method. The hook will then pull the data from the state store based on the `feed`.

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useFeedActivities` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useMembers

For feeds that have members (e.g. group feeds), the `useMembers` hook provides a reactive list of members, member count, pagination state, and a `loadNextPage` function to load more members.

The hook accepts an optional `feed`; when omitted, it uses the feed from the nearest `StreamFeedContext`.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useMembers } from "@stream-io/feeds-react-sdk";

const Members = ({ feed }: { feed: Feed }) => {
  const {
    members,
    member_count,
    member_pagination,
    is_loading_next_page,
    has_next_page,
    loadNextPage,
  } = useMembers(feed) ?? {};

  // or `useMembers()` if the component is wrapped within a `StreamFeedContext`

  return (
    <div>
      {members?.map((member) => (
        <div key={member.user.id}>{member.user.name}</div>
      ))}
      {has_next_page && (
        <button
          onClick={() => loadNextPage?.()}
          disabled={is_loading_next_page}
        >
          Load more
        </button>
      )}
    </div>
  );
};
```

To load more members you can call the `loadNextPage` method. The hook will then pull the data from the state store based on the `feed`.

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useMembers` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useOwnFollows

Whenever examining a specific `feed`, it is often useful to know if we have any feeds that we have created that follow it specifically. This is the main way typically followers and followings are considered and accounted for.

For this purpose, we have the `own_follows` state field which essentially gives us an array of `FollowResponse`s, where the `source_feed` property is going to be one of the feeds we've created and `target_feed` will be the `feed` we are examining.

A common common usecase is to have a specific `feed.groupId` that usually does the following (for example a `timeline` feed) and this information can then be leveraged to determine whether we are truly following the provided `feed` or not.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useOwnFollows } from "@stream-io/feeds-react-sdk";

const Follows = ({ feed }: { feed: Feed }) => {
  const { own_follows } = useOwnFollows(feed) ?? {};

  // or `useOwnFollows()` if the component is wrapped within a `StreamFeedContext`

  const ownFollow = useMemo(
    () =>
      ownFollows &&
      ownFollows.find(
        (follow: FollowResponse) => follow.source_feed.group_id === "timeline",
      ),
    [ownFollows],
  );

  const followStatus = ownFollow.status;

  if (followStatus === "accepted") {
    console.log(`I am following feed ${feed.id} !`);
  }

  if (followStatus === "pending") {
    console.log(`I have requested to follow ${feed.id} !`);
  }

  if (followStatus === "rejected") {
    console.log(`Feed ${feed.id} has rejected my follow request !`);
  }

  return <div>...</div>;
};
```

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useOwnFollows` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useOwnFollowings

`own_followings` tells if the owner of the feed follows any feeds that are owned by the current user.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useOwnFollowings } from "@stream-io/feeds-react-sdk";

const Follows = ({ feed }: { feed: Feed }) => {
  const { own_followings } = useOwnFollowings(feed) ?? {};

  // or `useOwnFollowings()` if the component is wrapped within a `StreamFeedContext`
};
```

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useOwnFollowings` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useAggregatedActivities

Whenever examining notification feeds, we typically want to group the notifications in a certain way - in order to prevent receiving 1000s of notifications in the UI. For this purpose, we can use the `aggregated_activities` field within a `Feed`.

This hook is responsible for providing the aggregated activities in a reactive manner, pagination as well as some pagination utility methods.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useAggregatedActivities } from "@stream-io/feeds-react-sdk";

const Notifications = ({ feed }: { feed: Feed }) => {
  const { aggregated_activities, is_loading, has_next_page, loadNextPage } =
    useAggregatedActivities(feed) ?? {};

  // or `useAggregatedActivities()` if the component is wrapped within a `StreamFeedContext`

  if (isLoading) {
    return <div>{/* loading spinner ui */}</div>;
  }

  return (
    <div>
      {aggregated_activities.map((activity) => {
        /* some ui */
      })}
    </div>
  );
};
```

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useAggregatedActivities` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useNotificationStatus

A very popular behaviour of in-app notifications is their read/seen status. For example, not having opened notifications might be considered as not having "seen" them, whereas clicking on them and navigating elsewhere might be considered as having "read" them.

For this purpose, we have the `notification_status` property. It contains data for both of these use-cases and the hook provides this data in a reactive manner.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useNotificationStatus } from "@stream-io/feeds-react-sdk";

const Notifications = ({ feed }: { feed: Feed }) => {
  const {
    unread,
    unseen,
    last_read_at,
    last_seen_at,
    read_activities,
    seen_activities,
  } = useNotificationStatus(feed) ?? {};

  // or `useNotificationStatus()` if the component is wrapped within a `StreamFeedContext`

  return (
    <div>
      {/* Unread */}
      <span>
        You have {unread} unread posts. The ids of the ones you have read are{" "}
        {read_activities.join(",")} and this happened on{" "}
        {last_read_at.toString()}.
      </span>

      {/* Unseen */}
      <span>
        You have {unseen} unseen posts. The ids of the ones you have seen are{" "}
        {seen_activities.join(",")} and this happened on{" "}
        {last_seen_at.toString()}.
      </span>
    </div>
  );
};
```

>
> **Info:** Since the `feed` argument is optional, you can either pass it to the `useNotificationStatus` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useOwnCapabilities

Permissions are important and they can change throughout the application lifetime, it's better to hide certain UI parts or disable certain inputs if current user does not have access to them.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { useOwnCapabilities } from "@stream-io/feeds-react-sdk";

const FeedComponent = ({ feed }: { feed: Feed }) => {
  const { can_read_feed: canReadFeed } = useOwnCapabilities(feed);
  // or `useOwnCapabilities()` if the component is wrapped within a `StreamFeedContext`

  if (!canReadFeed) {
    return <div>You don't have permission to read this feed</div>;
  }

  return <div id="feed">...</div>;
};
```

>
> **Info:** Note that it's important that the `feed` that's being passed to `useOwnCapabilities` has been properly initialized through the `Feed.getOrCreate` method. If the `feed` wasn't initialized, all of the permissions returned from the hook will default to `false`.
>
> Since the `feed` argument is optional, you can either pass it to the `useOwnCapabilities` hook or you can make sure the hook is used within the `StreamFeedContext` or within the `StreamFeed` wrapper. It is going to fetch the `Feed` instance from the context if it is not passed.
>

### useSearchQuery

In order to orchestrate how our search works, we often want to see what value the current `searchQuery` holds. This hook will make that easier.

```tsx label="React"
import type { SearchController } from "@stream-io/feeds-react-sdk";
import { useSearchQuery } from "@stream-io/feeds-react-sdk";

const SearchComponent = ({ controller }: { controller: SearchController }) => {
  const { searchQuery } = useSearchQuery(controller);
  // or `useSearchQuery()` if the component is wrapped within a `StreamSearchContext`

  return <SearchBar searchQuery={searchQuery} />;
};
```

>
> **Info:** Since the `searchController` argument is optional, you can either pass it to the `useSearchQuery` hook or you can make sure the hook is used within the `StreamSearchContext` or within the `StreamSearch` wrapper. It is going to fetch the `SearchController` instance from the context if it is not passed.
>

### useSearchSources

In order to facilitate different kinds of searches, we register various `SearchSource` instances to our main `SearchController`. Since this may change on the fly, it is useful to get a reactive list of these sources so that we can then decide how the UI behaves as they change. This hook helps with that.

```tsx label="React"
import type { SearchController } from "@stream-io/feeds-react-sdk";
import { useSearchSources } from "@stream-io/feeds-react-sdk";

const SearchComponent = ({ controller }: { controller: SearchController }) => {
  const { sources } = useSearchSources(controller) ?? {};
  // or `useSearchSources()` if the component is wrapped within a `StreamSearchContext`

  return (sources ?? []).map((source) => (
    <MySearchSource key={source.type} source={source} />
  ));
};
```

>
> **Info:** Since the `searchController` argument is optional, you can either pass it to the `useSearchSources` hook or you can make sure the hook is used within the `StreamSearchContext` or within the `StreamSearch` wrapper. It is going to fetch the `SearchController` instance from the context if it is not passed.
>

### useSearchResult

The most important thing when searching is to of course display the search results. The `useSearchResult` hook helps with that significantly, providing a reactive state that encapsulates this functionality as well as some utilities.

```tsx label="React"
import type { SearchSource } from "@stream-io/feeds-react-sdk";
import { useSearchResult } from "@stream-io/feeds-react-sdk";

const SearchResultsWrapper = ({ source }: { controller: SearchSource }) => {
  const { items, error, isLoading, hasNext, loadMore } =
    useSearchResult(source);
  // or `useSearchResult()` if the component is wrapped within a `StreamSearchResultsContext`

  if (error) {
    return <ErrorIndicator />;
  }

  if (isLoading && !items) {
    return <LoadingIndicator />;
  }

  return (
    <SearchResultsComponent
      items={items}
      loadMore={loadMore}
      isLoading={isLoading}
    />
  );
};
```

>
> **Info:** Since the `source` argument is optional, you can either pass it to the `useSearchResult` hook or you can make sure the hook is used within the `StreamSearchResultsContext` or within the `StreamSearchResults` wrapper. It is going to fetch the `SearchSource` instance from the context if it is not passed.
>

### useStateStore

This hook is something we call a "connector" or a "binding" between our `StateStore` API and a React library. If there's not an SDK-provided hook that returns a specific information when it comes to feed state, you can easily craft your own.

Here's the simplest `useStateStore` hook usage example:

```tsx label="React"
import type { Feed, FeedState } from "@stream-io/feeds-react-sdk";
import { useStateStore } from "@stream-io/feeds-react-sdk";

// Make sure to keep selector reference stable
const selector = (currentState: FeedState) => ({
  members: currentState.members,
});

const Component = ({ feed }: { feed: Feed }) => {
  const { members } = useStateStore(feed.state, selector);

  return (
    <>
      {members.map((member) => (
        <div key={member.user.id}>{member.user.name}</div>
      ))}
    </>
  );
};
```

In this example the component will re-render each time `members` (and only `members`) property changes always displaying most up-to-date UI.

You can extract selector, binding and transformations to a custom hook module and use it across your application.

## Utility Hooks

Within the SDK we also provide some utility hooks that are meant to make some common pain points easier to integrate.

There is currently only one utility hook available:

- `useCreateFeedsClient({ apiKey, tokenOrProvider, userData, options })` - takes care of the creation of an instance of `FeedsClient` and gracefully connecting a user to it

### useCreateFeedsClient

One of the most important things to get right in a brand new integration is how and when a `client` is created and how we connect a user to it. For that purpose, we expose a hook that should make this relatively trivial.

It takes the following properties:

- `apiKey` - the API key of your app
- `tokenOrProvider` - can be either an already available user `token` or a `tokenProvider` function that returns a user token
- `userData` - an instance of `UserRequest`
- `options` - an instance of `FeedsClientOptions`

```tsx label="React"
import type { UserRequest } from "@stream-io/feeds-react-sdk";
import { useCreateFeedsClient, StreamFeeds } from "@stream-io/feeds-react-sdk";

const apiKey = "123";
const tokenProvider = async (userId: string) => {
  const token = await fetchTokenFromSomewhere();
  return token;
};

const options = { timeout: 10000 };

const FeedComponent = (user: UserRequest) => {
  const tokenOrProvider = useCallback(async () => {
    return tokenProvider(user.id);
  }, [user.id]);

  // or alternatively, if we've already computed the token elsewhere
  // const tokenOrProvider = useMemo(() => user.token, [user.token]);

  const client = useCreateFeedsClient({
    apiKey,
    tokenOrProvider,
    userData,
    options,
  });

  if (!client) {
    return null;
  }

  return <StreamFeeds client={client}>{/* your content here */}</StreamFeeds>;
};
```

>
> **Info:** If the invocation of `client.connectUser` promise rejects, an error will be thrown by the hook. This is done by design as these errors are irrecoverable on our side and it is the responsibility of integrators to handle these errors however they see fit.
>
> Popular options are [`ErrorBoundary` wrappers](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) or perhaps custom error managers that either handle some fallback UI or let the user react to these errors.
>

## Contexts and Wrappers

The SDK also exposes some useful `React.Context`s as well as contextual hooks that can be used for ease of data flow and by underlying components as well.

### StreamFeedsContext

The `StreamFeedsContext` is the top-most context that should be applied within one's integration.

For its `value` it passes an instance of `FeedsClient`, which is also what its `Provider` expects.

It comes with a utility wrapper `StreamFeeds` that expects a single `prop` - which is `client`.

```tsx label="React"
import type { FeedsClient } from "@stream-io/feeds-react-sdk";
import { StreamFeeds } from "@stream-io/feeds-react-sdk";

const FeedsWrapperComponent = ({ client }: { client: FeedsClient }) => {
  return <StreamFeeds client={client}>{/* your content here */}</StreamFeeds>;
};
```

Anywhere within the wrapper, you can then use the `useFeedsClient` hook to get a hold of the value passed to `StreamFeeds`.

>
> **Info:** The `StreamFeeds` wrapper component is expected to wrap all content related to feeds in your app.
>
> You do not need to wrap your entire app with it, but rather simply make sure that all places using anything from our `react-bindings` package are wrapped within, as it is very likely that they'll depend on the `useFeedsClient` hook.
>

### StreamFeedContext

The `StreamFeedContext` is a context expected to wrap components that utilize a `Feed` instance heavily, for much easier use. It will also make all of the feed state hooks no longer require you to pass a `feed` to them (although you can still do so).

For its `value` it passes an instance of `Feed`, which is also what its `Provider` expects.

It comes with a utility wrapper `StreamFeed` that expects a single `prop` - which is `feed`.

```tsx label="React"
import type { Feed } from "@stream-io/feeds-react-sdk";
import { StreamFeed } from "@stream-io/feeds-react-sdk";

const FeedWrapperComponent = ({ feed }: { feed: Feed }) => {
  return <StreamFeed feed={feed}>{/* your content here */}</StreamFeed>;
};
```

Anywhere within the wrapper, you can then use the `useFeedContext` hook to get a hold of the value passed to `StreamFeed`.

>
> **Info:** The `StreamFeed` wrapper component is useful when it wraps all content related to a certain feed in your app, if possible.
>
> You do not need to wrap your entire app with it.
>

### StreamSearchContext

The `StreamSearchContext` is a context expected to wrap components that are related to the search functionality, for much easier use. It will also make all of the feed search state hooks no longer require you to pass a `SearchController` instance to them (although you can still do so).

For its `value` it passes an instance of `SearchController`, which is also what its `Provider` expects.

It comes with a utility wrapper `StreamSearch` that expects a single `prop` - which is `searchController`.

```tsx label="React"
import type { SearchController } from "@stream-io/feeds-react-sdk";
import {
  ActivitySearchSource,
  FeedSearchSource,
  UserSearchSource,
  StreamSearch,
} from "@stream-io/feeds-react-sdk";

const SearchWrapperComponent = () => {
  const client = useFeedsClient();
  const searchController = useMemo(() => {
    if (!client) {
      return undefined;
    }

    return new SearchController({
      sources: [
        new ActivitySearchSource(client),
        new FeedSearchSource(client),
        new UserSearchSource(client),
      ],
      config: { keepSingleActiveSource: true },
    });
  }, [client]);

  return (
    <StreamSearch searchController={searchController}>
      {/* your content here */}
    </StreamSearch>
  );
};
```

Anywhere within the wrapper, you can then use the `useSearchContext` hook to get a hold of the value passed to `StreamSearch`.

>
> **Info:** The `StreamSearch` wrapper component is useful when it wraps all content related the search functionality within your app, if possible.
>
> You do not need to wrap your entire app with it.
>

### StreamSearchResultsContext

The `StreamSearchResultsContext` is a context expected to wrap components that are related to a single search source within the search functionality, for much easier use. It will also make all of the feed search state hooks no longer require you to pass a `SearchSource` instance to them (although you can still do so).

For its `value` it passes an instance of `SearchSource`, which is also what its `Provider` expects.

It comes with a utility wrapper `StreamSearchResults` that expects a single `prop` - which is `source`.

```tsx label="React"
import type { SearchSource } from "@stream-io/feeds-react-sdk";
import { StreamSearchResults } from "@stream-io/feeds-react-sdk";

const SearchSourceWrapperComponent = ({ source }: { source: SearchSource }) => {
  return (
    <StreamSearchResults source={source}>
      {/* your content here */}
    </StreamSearchResults>
  );
};
```

Anywhere within the wrapper, you can then use the `useSearchResultsContext` hook to get a hold of the value passed to `StreamSearchResults`.

>
> **Info:** The `StreamSearchResults` wrapper component is useful when it wraps all content related the search functionality within your app, if possible.
>
> You do not need to wrap your entire app with it.
>

### StreamActivityWithStateUpdatesContext

The `StreamActivityWithStateUpdatesContext` is a context for components that work with a single activity and its live state (e.g. on an activity details page). It holds an `ActivityWithStateUpdates` instance so child components can access it without prop drilling.

For its `value` it passes an instance of `ActivityWithStateUpdates`, which is also what its `Provider` expects.

It comes with a utility wrapper `StreamActivityWithStateUpdates` that expects a single `prop` — `activityWithStateUpdates`.

```tsx label="React"
import type { ActivityWithStateUpdates } from "@stream-io/feeds-react-sdk";
import { StreamActivityWithStateUpdates } from "@stream-io/feeds-react-sdk";

const ActivityDetailsPage = ({
  activityWithStateUpdates,
}: {
  activityWithStateUpdates: ActivityWithStateUpdates;
}) => {
  return (
    <StreamActivityWithStateUpdates
      activityWithStateUpdates={activityWithStateUpdates}
    >
      {/* Your activity details content; children can use useActivityWithStateUpdatesContext() */}
    </StreamActivityWithStateUpdates>
  );
};
```

Anywhere within the wrapper, you can use the `useActivityWithStateUpdatesContext` hook to get the `ActivityWithStateUpdates` instance. This is useful when using hooks like `useActivityComments` with an activity from an activity details page (where you pass `activity` from context and omit `feed`).

>
> **Info:** Use the `StreamActivityWithStateUpdates` wrapper when rendering an activity details view or any subtree that is scoped to a single activity with live state updates. Child components can then call `useActivityWithStateUpdatesContext()` to access the activity and related hooks without receiving it via props.
>

## Selector Rules

### Stability

Make sure your selector is either defined outside your component's scope or is memoized through the use of `useMemo` or `useCallback` if it relies on some outside property before passing it down to the `useStateStore` hook. The instability of the selector has negative implications on your application's performance.

```tsx label="React"
import type {
  Feed,
  FeedState,
  ActivityResponse,
  CommentResponse,
} from "@stream-io/feeds-react-sdk";
import { useStateStore } from "@stream-io/feeds-react-sdk";

const Component = ({
  feed,
  entity,
}: {
  feed: Feed;
  entity: ActivityResponse | CommentResponse;
}) => {
  const id = entity.id;

  // don't do this 👎
  const selector = (currentState: FeedState) => ({
    comments: currentState.comments_by_entity_id[id].comments ?? [],
  });

  // do this instead 👍
  const selector = useCallback(
    (currentState: FeedState) => ({
      comments: currentState.comments_by_entity_id[id].comments ?? [],
    }),
    [id],
  );

  const { comments } = useStateStore(feed.state, selector);

  return (
    <>
      {members.map((member) => (
        <div key={member.user.id}>{member.user.name}</div>
      ))}
    </>
  );
};
```

>
> **Info:** Note that this is just an example, comments in feeds can nest many levels deep and to make work with them easy the SDK already comes with a `useActivityComments` hook which does exactly the same thing under the hood.
>

### Selector Output

Selectors run each time the state changes so the top-level properties of the selector's output should be easily comparable between state changes (using [`Object.is()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)), if you recreate complex values between selections, the component using this selector will re-render each time the state changes.

Selectors are only for pulling specific parts of the state from the "central" object, if you need transformations, do it in a separate step instead.

>
> **Warn:** Note that adding or removing keys dynamically between selections is not supported, make sure the amount of keys and keys itself stay the same between selections.
>

```tsx title="useMembersById.ts" label="React"
import type { Feed, FeedState } from "@stream-io/feeds-react-sdk";
import { useStateStore } from "@stream-io/feeds-react-sdk";

// don't do this 👎
const selector = ({ members }: FeedState) => ({
  membersById: members.reduce((newMembersById, member) => {
    newMembersById[member.user.id] = member;
    return newMembersById;
  }, {}),
});

const useMembersById = (feed: Feed) => {
  const { membersById } = useStateStore(feed.state, selector);

  return membersById;
};

// do this instead 👍
const selector = ({ members }: FeedState) => ({
  members,
});

const useMembersById = (feed: Feed) => {
  const { members } = useStateStore(feed.state, selector);

  return useMemo(
    () =>
      members.reduce((newMembersById, member) => {
        newMembersById[member.user.id] = member;
        return newMembersById;
      }, {}),
    [members],
  );
};
```

>
> **Info:** It is possible to do simple transformations within the selectors as long as the top level keys hold primitive values which are easy to compare between selections.
>

---

For the most recent version of this documentation, visit [https://getstream.io/activity-feeds/docs/react/contexts-and-hooks/](https://getstream.io/activity-feeds/docs/react/contexts-and-hooks/).