useOutcomeState
The useOutcomeState hook is used for maintain updated state for an outcome.
Outcomes within a condition have a state field that indicates their current state. For instance, OutcomeState.Active signifies the outcome is available for betting, while OutcomeState.Stopped indicates it has temporarily stopped accepting bets. This hook monitors this state and returns an isLocked flag, indicating whether placing a bet on this outcome is currently possible or not.
Usage
Before utilizing useOutcomeState, it is essential to initialize the FeedSocketProvider and ConditionUpdatesProvider:
import {
ChainProvider,
FeedSocketProvider,
ConditionUpdatesProvider,
} from '@azuro-org/sdk'
import { polygonAmoy } from 'viem/chains'
function Providers(props: { children: React.ReactNode }) {
const { children } = props
return (
<ChainProvider initialChainId={polygonAmoy.id}>
<FeedSocketProvider>
<ConditionUpdatesProvider>
{children}
</ConditionUpdatesProvider>
</FeedSocketProvider>
</ChainProvider>
)
}Example of usage: Outcome button.
We’ve retrieved markets with outcomes and rendered a button for each outcome and need to make sure that the outcome in a state to accept bets. The useOutcomeState hook offers a method to react to changes in the outcome state.
import { useOutcomeState } from '@azuro-org/sdk'
import { type MarketOutcome } from '@azuro-org/toolkit'
type OutcomeButtonProps = {
outcome: MarketOutcome
}
const OutcomeButton: React.FC<OutcomeButtonProps> = (props) => {
const { outcome } = props
const { conditionId, outcomeId, state: initialState, hidden } = outcome
const { state, odds, turnover, isLocked, isHidden } = useOutcomeState({
conditionId,
outcomeId,
initialState,
isInitiallyHidden: hidden,
initialOdds: outcome.odds,
})
if (isHidden) {
return null
}
return (
<button disabled={isLocked}>
{outcome.selectionName} {odds}
</button>
)
}Props
{
conditionId: string
outcomeId: string
initialState?: OutcomeState
isInitiallyHidden?: boolean // pass outcome.hidden from MarketOutcome
initialOdds?: number // pass outcome.odds from MarketOutcome
}The initialState is optional. If it’s not provided, the useOutcomeState hook will automatically retrieve the initial value.
Pass outcome.hidden from MarketOutcome as isInitiallyHidden. When true, the outcome starts as
hidden and stays hidden until an update reports hidden: false — see
How isHidden behaves below. Omit it and isHidden is undefined until the
feed reports it.
enum OutcomeState {
Active = 'Active',
Canceled = 'Canceled',
Stopped = 'Stopped',
Won = 'Won',
Lost = 'Lost'
}OutcomeState.Canceled means voided — not “missing from the feed”. A canceled outcome is one the
protocol voided: bets on it are refunded. When an outcome is absent from the feed, this hook falls back
to OutcomeState.Stopped (it used to fall back to OutcomeState.Canceled). Use
isOutcomeSettled to ask whether an outcome has a final
result — Won, Lost or Canceled — rather than comparing states by hand.
Return Value
{
state: OutcomeState
odds: number // live odds (0 until known); updated on every socket update
turnover: string // empty ('') until a live update arrives — not returned by REST
isHidden?: boolean // undefined until the feed has reported it
isLocked: boolean // true when outcome is not Active
isFetching: boolean // flag indicates initial state fetching
}This hook returns state, not data — unlike its condition-level counterpart
useConditionState.
What is taken from an update
odds and turnover are taken from every update. They are real in every message. turnover is only
ever populated by live updates ('' until the first one arrives — the state endpoint doesn’t return it).
state and isHidden are taken only from updates whose condition is Active. An update for an
inactive condition reports every one of its outcomes as Stopped, whatever they had actually settled
to — so trusting it would un-settle a won, lost or voided outcome each time its condition is suspended.
Such an update leaves state and isHidden alone and schedules a re-read from the state endpoint, which
is authoritative for per-outcome state. The re-read is limited to the messages that can carry news: the
move into an inactive state, and every report of a settled condition.
The consequence to design for: when a condition stops, there is a brief window — one state-endpoint
round trip — in which its outcomes still read their previous state. Condition-level locking is
unaffected and still immediate, so gate the bet on
useConditionState as well as on the outcome, and don’t
treat a per-outcome Stopped as instantaneous.
How isHidden behaves
isHidden says whether the feed is offering this outcome right now, independently of its condition’s own
hidden flag — a condition can be on offer with one of its outcomes withdrawn. It starts from
isInitiallyHidden — and from it again whenever the hook is
pointed at another outcome — and is latched one way, by
design: once the outcome has been reported visible it
stays visible, and nothing hides it again, so it doesn’t flicker in and out of the market as its condition
is suspended and re-priced. Render the lock from isLocked instead.
isHidden is boolean | undefined, and undefined means the feed has not reported it yet — not
“visible” and not “already revealed”. Only an explicit false closes the latch; if undefined counted
as revealed, the latch would close before the feed had said anything.
!isHidden is the right test for “show it” and treats undefined as visible. Don’t write
isHidden === false to mean “revealed”.
The SDK owns this latch now. If your app keeps its own one-way isHidden latch on top of this hook
to stop outcomes flickering, drop it — re-hiding no longer happens.
Pointing the hook at another outcome
Reusing one component instance across outcomes is safe — you don’t need a key to force a remount. When
conditionId or outcomeId changes, the hook re-seeds from the new props: state from initialState,
isHidden from isInitiallyHidden, odds from initialOdds. turnover starts empty again, isFetching
follows the new props, and the new outcome gets its own state-read attempts. The subscription follows
conditionId, so pointing the hook at an outcome of a different condition moves it too.
This is what makes isHidden trustworthy on a reused instance. Visibility is latched, so a reveal the
previous outcome had earned would otherwise carry over to the new one permanently, with nothing able to
take it back. state is exposed the same way: because state and isHidden are ignored on updates whose
condition is not Active (see What is taken from an update), the first
message about the new outcome cannot be relied on to correct a carried-over value.
A state read still in flight for the outcome that has gone is discarded rather than written into the new one, so a slow read can’t overwrite the outcome you are now watching with the answer for a previous one.