Fix some hydration issues (#1033)

* Extract `useIsClient` hook

* Fix a bunch of hydration bugs relevant to the contract page
This commit is contained in:
Marshall Polaris 2022-10-12 14:07:07 -07:00 committed by GitHub
parent aa717a767d
commit c44f223064
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 17 deletions

View File

@ -34,6 +34,7 @@ import { ExtraContractActionsRow } from './extra-contract-actions-row'
import { GroupLink } from 'common/group'
import { Subtitle } from '../subtitle'
import { useIsMobile } from 'web/hooks/use-is-mobile'
import { useIsClient } from 'web/hooks/use-is-client'
import {
BountiedContractBadge,
BountiedContractSmallBadge,
@ -52,22 +53,23 @@ export function MiscDetails(props: {
const { volume, closeTime, isResolved, createdTime, resolutionTime } =
contract
const isClient = useIsClient()
const isNew = createdTime > Date.now() - DAY_MS && !isResolved
const groupToDisplay = getGroupLinkToDisplay(contract)
return (
<Row className="items-center gap-3 truncate text-sm text-gray-400">
{showTime === 'close-date' ? (
{isClient && showTime === 'close-date' ? (
<Row className="gap-0.5 whitespace-nowrap">
<ClockIcon className="h-5 w-5" />
{(closeTime || 0) < Date.now() ? 'Closed' : 'Closes'}{' '}
{fromNow(closeTime || 0)}
</Row>
) : showTime === 'resolve-date' && resolutionTime !== undefined ? (
) : isClient && showTime === 'resolve-date' && resolutionTime ? (
<Row className="gap-0.5">
<ClockIcon className="h-5 w-5" />
{'Resolved '}
{fromNow(resolutionTime || 0)}
{fromNow(resolutionTime)}
</Row>
) : (contract?.featuredOnHomeRank ?? 0) > 0 ? (
<FeaturedContractBadge />
@ -390,6 +392,7 @@ function EditableCloseDate(props: {
}) {
const { closeTime, contract, isCreator, disabled } = props
const isClient = useIsClient()
const dayJsCloseTime = dayjs(closeTime)
const dayJsNow = dayjs()
@ -452,7 +455,7 @@ function EditableCloseDate(props: {
className="w-full shrink-0 sm:w-fit"
onClick={(e) => e.stopPropagation()}
onChange={(e) => setCloseDate(e.target.value)}
min={Date.now()}
min={isClient ? Date.now() : undefined}
value={closeDate}
/>
<Input
@ -479,14 +482,18 @@ function EditableCloseDate(props: {
</Col>
</Modal>
<DateTimeTooltip
text={closeTime > Date.now() ? 'Trading ends:' : 'Trading ended:'}
text={
isClient && closeTime <= Date.now()
? 'Trading ended:'
: 'Trading ends:'
}
time={closeTime}
>
<Row
className={clsx(!disabled && isCreator ? 'cursor-pointer' : '')}
onClick={() => !disabled && isCreator && setIsEditingCloseTime(true)}
>
{isSameDay ? (
{isSameDay && isClient ? (
<span className={'capitalize'}> {fromNow(closeTime)}</span>
) : isSameYear ? (
dayJsCloseTime.format('MMM D')

View File

@ -6,17 +6,19 @@ import { contractPath, getBinaryProbPercent } from 'web/lib/firebase/contracts'
import { fromNow } from 'web/lib/util/time'
import { BinaryContractOutcomeLabel } from '../outcome-label'
import { getColor } from './quick-bet'
import { useIsClient } from 'web/hooks/use-is-client'
export function ContractMention(props: { contract: Contract }) {
const { contract } = props
const { outcomeType, resolution } = contract
const probTextColor = `text-${getColor(contract)}`
const isClient = useIsClient()
return (
<Link href={contractPath(contract)}>
<a
className="group inline whitespace-nowrap rounded-sm hover:bg-indigo-50 focus:bg-indigo-50"
title={tooltipLabel(contract)}
title={isClient ? tooltipLabel(contract) : undefined}
>
<span className="break-anywhere mr-0.5 whitespace-normal font-normal text-indigo-700">
{contract.question}

View File

@ -5,6 +5,7 @@ import Link from 'next/link'
import { fromNow } from 'web/lib/util/time'
import { ToastClipboard } from 'web/components/toast-clipboard'
import { LinkIcon } from '@heroicons/react/outline'
import { useIsClient } from 'web/hooks/use-is-client'
export function CopyLinkDateTimeComponent(props: {
prefix: string
@ -14,6 +15,7 @@ export function CopyLinkDateTimeComponent(props: {
className?: string
}) {
const { prefix, slug, elementId, createdTime, className } = props
const isClient = useIsClient()
const [showToast, setShowToast] = useState(false)
function copyLinkToComment(
@ -36,7 +38,7 @@ export function CopyLinkDateTimeComponent(props: {
'text-greyscale-4 hover:bg-greyscale-1.5 mx-1 whitespace-nowrap rounded-sm px-1 text-xs transition-colors'
}
>
{fromNow(createdTime)}
{isClient && fromNow(createdTime)}
{showToast && <ToastClipboard />}
<LinkIcon className="ml-1 mb-0.5 inline" height={13} />
</a>

View File

@ -1,22 +1,16 @@
import { DateTimeTooltip } from './datetime-tooltip'
import React, { useEffect, useState } from 'react'
import { fromNow } from 'web/lib/util/time'
import { useIsClient } from 'web/hooks/use-is-client'
export function RelativeTimestamp(props: { time: number }) {
const { time } = props
const [isClient, setIsClient] = useState(false)
useEffect(() => {
// Only render on client to prevent difference from server.
setIsClient(true)
}, [])
const isClient = useIsClient()
return (
<DateTimeTooltip
className="ml-1 whitespace-nowrap text-gray-400"
time={time}
>
{isClient ? fromNow(time) : ''}
{isClient && fromNow(time)}
</DateTimeTooltip>
)
}

View File

@ -0,0 +1,7 @@
import { useEffect, useState } from 'react'
export const useIsClient = () => {
const [isClient, setIsClient] = useState(false)
useEffect(() => setIsClient(true), [])
return isClient
}