# Poll hooks

> **Info:** This is release candidate documentation for **Stream Chat React SDK v15**. For the latest stable version, see the [latest version (v14)](https://getstream.io/chat/docs/sdk/react/).

These hooks give a component the poll it renders, and page through the poll's comments and votes. They're exported from `stream-chat-react`.

Polls are explained on [Polls](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/polls/), and the poll components and `PollContext` on [Poll components](https://getstream.io/chat/docs/sdk/react/v15/component-reference/poll-components/).

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.

## usePollContext

Returns the poll the component renders.

```ts
usePollContext(): PollContextValue
```

- **Returns:** `{ poll }`: the `Poll` of the nearest `Poll` component or `PollProvider`.
- **Reads:** the context of `PollProvider`, which `Poll` renders. No store: the poll's data is in `poll.state`, which you read with `useStateStore`.
- **Re-renders:** whenever the `PollProvider` above renders, since it passes a new value each time. Votes and other changes in `poll.state` don't re-render it by themselves.
- **Needs:** a `Poll` or a `PollProvider`. Throws outside both.

```tsx
import type { PollState } from "stream-chat";
import { usePollContext, useStateStore } from "stream-chat-react";

const statusSelector = ({ is_closed, vote_count }: PollState) => ({
  isClosed: is_closed,
  voteCount: vote_count,
});

export const PollStatus = () => {
  const { poll } = usePollContext();
  const { isClosed, voteCount } = useStateStore(poll.state, statusSelector);

  return (
    <span>
      {voteCount} votes{isClosed ? ", closed" : ""}
    </span>
  );
};
```

The default `MessageUI` renders `Poll` for each message with a poll, so a component given to one of the poll override keys sits inside one. To render a poll's parts elsewhere, look the poll up with `client.polls.fromState(pollId)` and wrap them in `<PollProvider poll={poll}>`. `PollProvider` renders nothing while the poll is `undefined`.

## usePollAnswerPagination

Loads the poll's comments, a page at a time.

```ts
usePollAnswerPagination(params?: { paginationParams?: PollAnswersQueryParams }): {
  answers: PollVoteResponseData[];
  error: Error | undefined;
  hasNextPage: boolean;
  loading: boolean;
  loadMore: () => Promise<void>;
}
```

`paginationParams` takes a `filter`, `options` such as `limit`, and a `sort`.

- **Returns:**
  - `answers`: the comments loaded so far, newest first. The hook sorts by `created_at`, newest first, before your `sort`.
  - `loadMore()`: loads the next page. It does nothing while a page is loading or after the last one.
  - `hasNextPage` and `loading`: where the list stands.
  - `error`: the error of the last request that failed. A page that loads after it doesn't clear it.
- **Reads:** `Poll.queryAnswers()`, into a list the hook keeps for the component. It loads the first page when it mounts. The `poll.vote_casted`, `poll.vote_changed` and `poll.vote_removed` events add, replace and remove comments after that.
- **Re-renders:** when a page starts loading, loads or fails, and when a comment event arrives.
- **Needs:** `Chat`, and a `Poll` or a `PollProvider`. Throws outside them.

The list loads once. New `paginationParams` don't reload it: render the component again with a new `key` to start over. `PollAnswerList`, the View Comments dialog, uses this hook.

```tsx
import { usePollAnswerPagination } from "stream-chat-react";

export const PollComments = () => {
  const { answers, hasNextPage, loading, loadMore } = usePollAnswerPagination();

  return (
    <div>
      <ul>
        {answers.map((answer) => (
          <li key={answer.id}>
            {answer.user?.name}: {answer.answer_text}
          </li>
        ))}
      </ul>
      {hasNextPage && (
        <button
          disabled={loading}
          onClick={() => void loadMore()}
          type="button"
        >
          More comments
        </button>
      )}
    </div>
  );
};
```

## usePollOptionVotesPagination

Loads the votes for one option, a page at a time.

```ts
usePollOptionVotesPagination(params: { paginationParams: PollOptionVotesQueryParams }): {
  error: Error | undefined;
  hasNextPage: boolean;
  loading: boolean;
  loadMore: () => Promise<void>;
  votes: PollVoteResponseData[];
}
```

`paginationParams.filter.option_id` names the option, and is required.

- **Returns:**
  - `votes`: the option's votes loaded so far, newest first, as for `usePollAnswerPagination`.
  - `loadMore()`, `hasNextPage`, `loading` and `error`: as for `usePollAnswerPagination`. `options.limit` sets the size of the first page. Every page after it holds up to 5 votes.
- **Reads:** `Poll.queryOptionVotes()`, into a list the hook keeps for the component. It loads the first page when it mounts, and follows the vote events for that option.
- **Re-renders:** when a page starts loading, loads or fails, and when a vote for the option is cast, changed or removed.
- **Needs:** `Chat`, and a `Poll` or a `PollProvider`. Throws outside them.

Pass `paginationParams` with a stable identity, as `PollResults` does for each option:

```tsx
import { useMemo } from "react";
import type { PollOptionVotesQueryParams } from "stream-chat";
import { usePollOptionVotesPagination } from "stream-chat-react";

export const OptionVoters = ({ optionId }: { optionId: string }) => {
  const paginationParams = useMemo<PollOptionVotesQueryParams>(
    () => ({ filter: { option_id: optionId } }),
    [optionId],
  );
  const { hasNextPage, loadMore, votes } = usePollOptionVotesPagination({
    paginationParams,
  });

  return (
    <div>
      {votes.map((vote) => (
        <span key={vote.id}>{vote.user?.name}</span>
      ))}
      {hasNextPage && (
        <button onClick={() => void loadMore()} type="button">
          Show more
        </button>
      )}
    </div>
  );
};
```

Without `useMemo`, a first page that failed would be requested again on every render. A new `optionId` still doesn't reload the votes: where the option can change, render `OptionVoters` with `key={optionId}`.

## Poll state without a hook

`stream-chat-react` has no hook for a poll's own state. Read `poll.state` with `useStateStore`, with the poll from `usePollContext()`, as `PollStatus` does above. It holds the poll's data and a few values derived from it:

| To read                      | Field in `poll.state`                 |
| ---------------------------- | ------------------------------------- |
| The question and the options | `name`, `options`                     |
| The vote counts              | `vote_count`, `vote_counts_by_option` |
| The leading options          | `maxVotedOptionIds`                   |
| The current user's votes     | `ownVotesByOptionId`                  |
| The current user's comment   | `ownAnswer`                           |
| Whether voting has ended     | `is_closed`                           |

[Voting and keeping the poll current](https://getstream.io/chat/docs/sdk/react/v15/features/rich-content/polls/#voting-and-keeping-the-poll-current) explains how votes and events change it. The poll being created in the composer is in `pollComposer.state`: see [Composer hooks](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/composer-hooks/#composer-stores-without-a-hook).

---

For the most recent version of this documentation, visit [https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/poll-hooks/](https://getstream.io/chat/docs/sdk/react/v15/reference/hooks/poll-hooks/).