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

Poll hooks

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, and the poll components and PollContext on 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.

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.
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.

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.

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.

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:

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 explains how votes and events change it. The poll being created in the composer is in pollComposer.state: see Composer hooks.