diff --git a/package.json b/package.json index fc49705e9bb..b5297acf74c 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "build:docs": "PARCEL_WORKER_BACKEND=process DOCS_ENV=staging parcel build 'packages/@react-{spectrum,aria,stately}/*/docs/*.mdx' 'packages/dev/docs/pages/**/*.mdx' --no-scope-hoist", "test": "yarn jest", "test:ssr": "yarn jest --config jest.ssr.config.js", - "ci-test": "yarn jest --maxWorkers=2", + "ci-test": "yarn jest --maxWorkers=2 && yarn test:ssr --maxWorkers=2", "lint": "yarn check-types && eslint packages --ext .js,.ts,.tsx && node scripts/lint-packages.js", "jest": "node scripts/jest.js", "copyrights": "babel-node --presets @babel/env ./scripts/addHeaders.js", diff --git a/packages/@react-aria/focus/src/FocusScope.tsx b/packages/@react-aria/focus/src/FocusScope.tsx index e8c7a1ad800..49b9db0517c 100644 --- a/packages/@react-aria/focus/src/FocusScope.tsx +++ b/packages/@react-aria/focus/src/FocusScope.tsx @@ -11,7 +11,8 @@ */ import {focusSafely} from './focusSafely'; -import React, {ReactNode, RefObject, useContext, useEffect, useLayoutEffect, useRef} from 'react'; +import React, {ReactNode, RefObject, useContext, useEffect, useRef} from 'react'; +import {useLayoutEffect} from '@react-aria/utils'; // import {FocusScope, useFocusScope} from 'react-events/focus-scope'; // export {FocusScope}; diff --git a/packages/@react-aria/overlays/src/useOverlayPosition.ts b/packages/@react-aria/overlays/src/useOverlayPosition.ts index 9c5c0beec2b..3f4955ca2da 100644 --- a/packages/@react-aria/overlays/src/useOverlayPosition.ts +++ b/packages/@react-aria/overlays/src/useOverlayPosition.ts @@ -65,7 +65,7 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria { placement = 'bottom' as Placement, containerPadding = 12, shouldFlip = true, - boundaryElement = document.body, + boundaryElement = typeof document !== 'undefined' ? document.body : null, offset = 0, crossOffset = 0, shouldUpdatePosition = true, @@ -95,7 +95,7 @@ export function useOverlayPosition(props: AriaPositionProps): PositionAria { ]; let updatePosition = useCallback(() => { - if (shouldUpdatePosition === false || !isOpen || !overlayRef.current || !targetRef.current || !scrollRef.current) { + if (shouldUpdatePosition === false || !isOpen || !overlayRef.current || !targetRef.current || !scrollRef.current || !boundaryElement) { return; } diff --git a/packages/@react-aria/radio/src/useRadio.ts b/packages/@react-aria/radio/src/useRadio.ts index 61f5ef8ca5f..ce4dbbb1a00 100644 --- a/packages/@react-aria/radio/src/useRadio.ts +++ b/packages/@react-aria/radio/src/useRadio.ts @@ -13,6 +13,7 @@ import {AriaRadioProps} from '@react-types/radio'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; import {InputHTMLAttributes, RefObject} from 'react'; +import {radioGroupNames} from './utils'; import {RadioGroupState} from '@react-stately/radio'; import {useFocusable} from '@react-aria/focus'; import {usePress} from '@react-aria/interactions'; @@ -82,7 +83,7 @@ export function useRadio(props: RadioAriaProps, state: RadioGroupState, ref: Ref inputProps: mergeProps(domProps, { ...interactions, type: 'radio', - name: state.name, + name: radioGroupNames.get(state), tabIndex, disabled: isDisabled, readOnly: isReadOnly, diff --git a/packages/@react-aria/radio/src/useRadioGroup.ts b/packages/@react-aria/radio/src/useRadioGroup.ts index 16f94621f53..ba327a12aaa 100644 --- a/packages/@react-aria/radio/src/useRadioGroup.ts +++ b/packages/@react-aria/radio/src/useRadioGroup.ts @@ -11,9 +11,10 @@ */ import {AriaRadioGroupProps} from '@react-types/radio'; -import {filterDOMProps, mergeProps} from '@react-aria/utils'; +import {filterDOMProps, mergeProps, useId} from '@react-aria/utils'; import {getFocusableTreeWalker} from '@react-aria/focus'; import {HTMLAttributes} from 'react'; +import {radioGroupNames} from './utils'; import {RadioGroupState} from '@react-stately/radio'; import {useFocusWithin} from '@react-aria/interactions'; import {useLabel} from '@react-aria/label'; @@ -34,6 +35,7 @@ interface RadioGroupAria { */ export function useRadioGroup(props: AriaRadioGroupProps, state: RadioGroupState): RadioGroupAria { let { + name, validationState, isReadOnly, isRequired, @@ -111,6 +113,9 @@ export function useRadioGroup(props: AriaRadioGroupProps, state: RadioGroupState } }; + let groupName = useId(name); + radioGroupNames.set(state, groupName); + return { radioGroupProps: mergeProps(domProps, { // https://www.w3.org/TR/wai-aria-1.2/#radiogroup diff --git a/packages/@react-aria/radio/src/utils.ts b/packages/@react-aria/radio/src/utils.ts new file mode 100644 index 00000000000..0865138d7b9 --- /dev/null +++ b/packages/@react-aria/radio/src/utils.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2020 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import {RadioGroupState} from '@react-stately/radio'; + +export const radioGroupNames = new WeakMap(); diff --git a/packages/@react-aria/ssr/src/SSRProvider.tsx b/packages/@react-aria/ssr/src/SSRProvider.tsx index 51aa5bd0345..7a71f2bbbfb 100644 --- a/packages/@react-aria/ssr/src/SSRProvider.tsx +++ b/packages/@react-aria/ssr/src/SSRProvider.tsx @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import React, {ReactNode, useContext, useMemo} from 'react'; +import React, {ReactNode, useContext, useLayoutEffect, useMemo, useState} from 'react'; // To support SSR, the auto incrementing id counter is stored in a context. This allows // it to be reset on every request to ensure the client and server are consistent. @@ -77,3 +77,23 @@ export function useSSRSafeId(defaultId?: string): string { return useMemo(() => defaultId || `react-aria-${ctx.prefix}-${++ctx.current}`, [defaultId]); } + +/** @private */ +export function useIsSSR(): boolean { + let cur = useContext(SSRContext); + let isInSSRContext = cur !== defaultContext; + let [isSSR, setIsSSR] = useState(isInSSRContext); + + // If on the client, and the component was initially server rendered, + // then schedule a layout effect to update the component after hydration. + if (typeof window !== 'undefined' && isInSSRContext) { + // This if statement technically breaks the rules of hooks, but is safe + // because the condition never changes after mounting. + // eslint-disable-next-line react-hooks/rules-of-hooks + useLayoutEffect(() => { + setIsSSR(false); + }, []); + } + + return isSSR; +} diff --git a/packages/@react-aria/utils/src/index.ts b/packages/@react-aria/utils/src/index.ts index bb93565152f..ea43672985d 100644 --- a/packages/@react-aria/utils/src/index.ts +++ b/packages/@react-aria/utils/src/index.ts @@ -21,3 +21,4 @@ export * from './useUpdateEffect'; export * from './focusWithoutScrolling'; export * from './filterDOMProps'; export * from './runAfterTransition'; +export * from './useLayoutEffect'; diff --git a/packages/@react-aria/utils/src/useId.ts b/packages/@react-aria/utils/src/useId.ts index 05ac1bab28b..0ca99c6efb9 100644 --- a/packages/@react-aria/utils/src/useId.ts +++ b/packages/@react-aria/utils/src/useId.ts @@ -10,8 +10,9 @@ * governing permissions and limitations under the License. */ -import {useLayoutEffect, useState} from 'react'; +import {useLayoutEffect} from './useLayoutEffect'; import {useSSRSafeId} from '@react-aria/ssr'; +import {useState} from 'react'; let map: Map void> = new Map(); diff --git a/packages/@react-aria/utils/src/useLayoutEffect.ts b/packages/@react-aria/utils/src/useLayoutEffect.ts new file mode 100644 index 00000000000..80c09a339b9 --- /dev/null +++ b/packages/@react-aria/utils/src/useLayoutEffect.ts @@ -0,0 +1,20 @@ +/* + * Copyright 2020 Adobe. All rights reserved. + * This file is licensed to you under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. You may obtain a copy + * of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS + * OF ANY KIND, either express or implied. See the License for the specific language + * governing permissions and limitations under the License. + */ + +import React from 'react'; + +// During SSR, React emits a warning when calling useLayoutEffect. +// Since neither useLayoutEffect nor useEffect run on the server, +// we can suppress this by replace it with a noop on the server. +export const useLayoutEffect = typeof window !== 'undefined' + ? React.useLayoutEffect + : () => {}; diff --git a/packages/@react-aria/virtualizer/src/ScrollView.tsx b/packages/@react-aria/virtualizer/src/ScrollView.tsx index b03f5d23698..2db526ff981 100644 --- a/packages/@react-aria/virtualizer/src/ScrollView.tsx +++ b/packages/@react-aria/virtualizer/src/ScrollView.tsx @@ -20,11 +20,11 @@ import React, { RefObject, useCallback, useEffect, - useLayoutEffect, useRef, useState } from 'react'; import {Rect, Size} from '@react-stately/virtualizer'; +import {useLayoutEffect} from '@react-aria/utils'; import {useLocale} from '@react-aria/i18n'; interface ScrollViewProps extends HTMLAttributes { diff --git a/packages/@react-aria/virtualizer/src/useVirtualizerItem.ts b/packages/@react-aria/virtualizer/src/useVirtualizerItem.ts index e3a6af01923..a40603fa38d 100644 --- a/packages/@react-aria/virtualizer/src/useVirtualizerItem.ts +++ b/packages/@react-aria/virtualizer/src/useVirtualizerItem.ts @@ -10,8 +10,9 @@ * governing permissions and limitations under the License. */ -import {RefObject, useCallback, useLayoutEffect} from 'react'; +import {RefObject, useCallback} from 'react'; import {ReusableView, Size} from '@react-stately/virtualizer'; +import {useLayoutEffect} from '@react-aria/utils'; interface VirtualizerItemOptions { reusableView: ReusableView, diff --git a/packages/@react-spectrum/breadcrumbs/src/Breadcrumbs.tsx b/packages/@react-spectrum/breadcrumbs/src/Breadcrumbs.tsx index 48822c8eae0..5b0dbeb1c94 100644 --- a/packages/@react-spectrum/breadcrumbs/src/Breadcrumbs.tsx +++ b/packages/@react-spectrum/breadcrumbs/src/Breadcrumbs.tsx @@ -15,10 +15,11 @@ import {classNames, useDOMRef, useResizeObserver, useStyleProps} from '@react-sp import {DOMRef} from '@react-types/shared'; import FolderBreadcrumb from '@spectrum-icons/ui/FolderBreadcrumb'; import {Menu, MenuTrigger} from '@react-spectrum/menu'; -import React, {Key, ReactElement, useCallback, useLayoutEffect, useRef, useState} from 'react'; +import React, {Key, ReactElement, useCallback, useRef, useState} from 'react'; import {SpectrumBreadcrumbsProps} from '@react-types/breadcrumbs'; import styles from '@adobe/spectrum-css-temp/components/breadcrumb/vars.css'; import {useBreadcrumbs} from '@react-aria/breadcrumbs'; +import {useLayoutEffect} from '@react-aria/utils'; import {useProviderProps} from '@react-spectrum/provider'; const MIN_VISIBLE_ITEMS = 1; diff --git a/packages/@react-spectrum/buttongroup/src/ButtonGroup.tsx b/packages/@react-spectrum/buttongroup/src/ButtonGroup.tsx index 89eb8cd019f..d4255783f27 100644 --- a/packages/@react-spectrum/buttongroup/src/ButtonGroup.tsx +++ b/packages/@react-spectrum/buttongroup/src/ButtonGroup.tsx @@ -12,8 +12,8 @@ import {classNames, SlotProvider, useDOMRef, useSlotProps, useStyleProps} from '@react-spectrum/utils'; import {DOMRef} from '@react-types/shared'; -import {filterDOMProps} from '@react-aria/utils'; -import React, {useCallback, useEffect, useLayoutEffect, useState} from 'react'; +import {filterDOMProps, useLayoutEffect} from '@react-aria/utils'; +import React, {useCallback, useEffect, useState} from 'react'; import {SpectrumButtonGroupProps} from '@react-types/buttongroup'; import styles from '@adobe/spectrum-css-temp/components/buttongroup/vars.css'; import {useProvider, useProviderProps} from '@react-spectrum/provider'; diff --git a/packages/@react-spectrum/combobox/package.json b/packages/@react-spectrum/combobox/package.json index 6192788e8cc..ce58a3bd726 100644 --- a/packages/@react-spectrum/combobox/package.json +++ b/packages/@react-spectrum/combobox/package.json @@ -37,6 +37,7 @@ "@react-aria/focus": "^3.1.0", "@react-aria/i18n": "^3.1.0", "@react-aria/overlays": "^3.1.0", + "@react-aria/utils": "3.2.0", "@react-spectrum/button": "^3.1.0", "@react-spectrum/label": "^3.1.0", "@react-spectrum/listbox": "^3.1.0", diff --git a/packages/@react-spectrum/combobox/src/ComboBox.tsx b/packages/@react-spectrum/combobox/src/ComboBox.tsx index fcf984b88d7..67b0b747c45 100644 --- a/packages/@react-spectrum/combobox/src/ComboBox.tsx +++ b/packages/@react-spectrum/combobox/src/ComboBox.tsx @@ -21,7 +21,7 @@ import labelStyles from '@adobe/spectrum-css-temp/components/fieldlabel/vars.css import {ListBoxBase, useListBoxLayout} from '@react-spectrum/listbox'; import {Placement} from '@react-types/overlays'; import {Popover, Tray} from '@react-spectrum/overlays'; -import React, {ReactElement, RefObject, useLayoutEffect, useRef, useState} from 'react'; +import React, {ReactElement, RefObject, useRef, useState} from 'react'; import {SpectrumComboBoxProps} from '@react-types/combobox'; import styles from '@adobe/spectrum-css-temp/components/inputgroup/vars.css'; import {TextFieldBase} from '@react-spectrum/textfield'; @@ -29,6 +29,7 @@ import {TextFieldRef} from '@react-types/textfield'; import {useCollator} from '@react-aria/i18n'; import {useComboBox} from '@react-aria/combobox'; import {useComboBoxState} from '@react-stately/combobox'; +import {useLayoutEffect} from '@react-aria/utils'; import {useProvider, useProviderProps} from '@react-spectrum/provider'; function ComboBox(props: SpectrumComboBoxProps, ref: RefObject) { diff --git a/packages/@react-spectrum/layout/package.json b/packages/@react-spectrum/layout/package.json index 31cc6508210..77f1e26a0ef 100644 --- a/packages/@react-spectrum/layout/package.json +++ b/packages/@react-spectrum/layout/package.json @@ -32,6 +32,7 @@ }, "dependencies": { "@babel/runtime": "^7.6.2", + "@react-aria/ssr": "3.0.0-alpha.1", "@react-aria/utils": "^3.2.0", "@react-spectrum/utils": "^3.2.0", "@react-types/layout": "^3.1.0", diff --git a/packages/@react-spectrum/layout/src/Flex.tsx b/packages/@react-spectrum/layout/src/Flex.tsx index 95bf2ffeb25..6ccc8024d11 100644 --- a/packages/@react-spectrum/layout/src/Flex.tsx +++ b/packages/@react-spectrum/layout/src/Flex.tsx @@ -15,7 +15,8 @@ import {DOMRef} from '@react-types/shared'; import {filterDOMProps} from '@react-aria/utils'; import {FlexProps} from '@react-types/layout'; import React, {forwardRef} from 'react'; -import styles from './flex.css'; +import styles from './flex-gap.css'; +import {useIsSSR} from '@react-aria/ssr'; const flexStyleProps: StyleHandlers = { direction: ['flexDirection', passthroughStyle], @@ -33,14 +34,15 @@ function Flex(props: FlexProps, ref: DOMRef) { let {styleProps} = useStyleProps(otherProps); let {styleProps: flexStyle} = useStyleProps(otherProps, flexStyleProps); let domRef = useDOMRef(ref); + let isSSR = useIsSSR(); - // If a gap property is specified, and there is no native support, use a shim. + // If a gap property is specified, and there is no native support or we're in SSR, use a shim. // Two divs are required for this: the outer one contains most style properties, and the inner // one is the flex container. Each item inside the flex container gets a margin around it based // on the gap, and the flex container has a negative margin to counteract this. The outer container // is necessary to allow nesting of flex containers with gaps, so that the inner CSS variable doesn't // override the outer one. - if ((props.gap || props.rowGap || props.columnGap) && !isFlexGapSupported()) { + if ((props.gap || props.rowGap || props.columnGap) && (isSSR || !isFlexGapSupported())) { let style = { ...flexStyle.style, '--column-gap': props.columnGap != null ? dimensionValue(props.columnGap) : undefined, diff --git a/packages/@react-spectrum/layout/src/flex.css b/packages/@react-spectrum/layout/src/flex-gap.css similarity index 100% rename from packages/@react-spectrum/layout/src/flex.css rename to packages/@react-spectrum/layout/src/flex-gap.css diff --git a/packages/@react-spectrum/listbox/test/ListBox.ssr.test.js b/packages/@react-spectrum/listbox/test/ListBox.ssr.test.js index 6e3893ab062..76123cc6a35 100644 --- a/packages/@react-spectrum/listbox/test/ListBox.ssr.test.js +++ b/packages/@react-spectrum/listbox/test/ListBox.ssr.test.js @@ -16,11 +16,16 @@ describe('ListBox SSR', function () { it('should render without errors', async function () { await testSSR(__filename, ` import {ListBox, Item} from '../'; - - Left - Middle - Right - + import {Provider} from '@react-spectrum/provider'; + import {theme} from '@react-spectrum/theme-default'; + + + + Left + Middle + Right + + `); }); }); diff --git a/packages/@react-spectrum/overlays/src/Popover.tsx b/packages/@react-spectrum/overlays/src/Popover.tsx index 56c9d5b8cd8..4f32405f90c 100644 --- a/packages/@react-spectrum/overlays/src/Popover.tsx +++ b/packages/@react-spectrum/overlays/src/Popover.tsx @@ -12,11 +12,11 @@ import {classNames, useDOMRef, useStyleProps} from '@react-spectrum/utils'; import {DOMRef} from '@react-types/shared'; -import {mergeProps} from '@react-aria/utils'; +import {mergeProps, useLayoutEffect} from '@react-aria/utils'; import {Overlay} from './Overlay'; import overrideStyles from './overlays.css'; import {PlacementAxis, PopoverProps} from '@react-types/overlays'; -import React, {forwardRef, HTMLAttributes, ReactNode, RefObject, useLayoutEffect, useRef, useState} from 'react'; +import React, {forwardRef, HTMLAttributes, ReactNode, RefObject, useRef, useState} from 'react'; import styles from '@adobe/spectrum-css-temp/components/popover/vars.css'; import {useModal, useOverlay} from '@react-aria/overlays'; diff --git a/packages/@react-spectrum/picker/src/Picker.tsx b/packages/@react-spectrum/picker/src/Picker.tsx index 956028fc8ab..69e57181382 100644 --- a/packages/@react-spectrum/picker/src/Picker.tsx +++ b/packages/@react-spectrum/picker/src/Picker.tsx @@ -23,12 +23,12 @@ import intlMessages from '../intl/*.json'; import {Label} from '@react-spectrum/label'; import labelStyles from '@adobe/spectrum-css-temp/components/fieldlabel/vars.css'; import {ListBoxBase, useListBoxLayout} from '@react-spectrum/listbox'; -import {mergeProps} from '@react-aria/utils'; +import {mergeProps, useLayoutEffect} from '@react-aria/utils'; import {Placement} from '@react-types/overlays'; import {Popover, Tray} from '@react-spectrum/overlays'; import {PressResponder, useHover} from '@react-aria/interactions'; import {ProgressCircle} from '@react-spectrum/progress'; -import React, {ReactElement, useLayoutEffect, useRef, useState} from 'react'; +import React, {ReactElement, useRef, useState} from 'react'; import {SpectrumPickerProps} from '@react-types/select'; import styles from '@adobe/spectrum-css-temp/components/dropdown/vars.css'; import {Text} from '@react-spectrum/text'; diff --git a/packages/@react-spectrum/picker/test/Picker.ssr.test.js b/packages/@react-spectrum/picker/test/Picker.ssr.test.js index 420c3fe955d..6eefd1c2622 100644 --- a/packages/@react-spectrum/picker/test/Picker.ssr.test.js +++ b/packages/@react-spectrum/picker/test/Picker.ssr.test.js @@ -16,11 +16,16 @@ describe('Picker SSR', function () { it('should render without errors', async function () { await testSSR(__filename, ` import {Picker, Item} from '../'; - - One - Two - Three - + import {Provider} from '@react-spectrum/provider'; + import {theme} from '@react-spectrum/theme-default'; + + + + One + Two + Three + + `); }); }); diff --git a/packages/@react-spectrum/tabs/src/Tabs.tsx b/packages/@react-spectrum/tabs/src/Tabs.tsx index f89473de281..8dc769f4660 100644 --- a/packages/@react-spectrum/tabs/src/Tabs.tsx +++ b/packages/@react-spectrum/tabs/src/Tabs.tsx @@ -13,8 +13,8 @@ import {classNames, useStyleProps} from '@react-spectrum/utils'; import {DOMProps, Node, Orientation, StyleProps} from '@react-types/shared'; import {FocusRing} from '@react-aria/focus'; -import {mergeProps} from '@react-aria/utils'; -import React, {useEffect, useLayoutEffect, useRef, useState} from 'react'; +import {mergeProps, useLayoutEffect} from '@react-aria/utils'; +import React, {useEffect, useRef, useState} from 'react'; import {SingleSelectListState, useSingleSelectListState} from '@react-stately/list'; import {SpectrumTabsProps} from '@react-types/tabs'; import styles from '@adobe/spectrum-css-temp/components/tabs/vars.css'; diff --git a/packages/@react-spectrum/textfield/src/TextArea.tsx b/packages/@react-spectrum/textfield/src/TextArea.tsx index 1837131061f..f486065c875 100644 --- a/packages/@react-spectrum/textfield/src/TextArea.tsx +++ b/packages/@react-spectrum/textfield/src/TextArea.tsx @@ -10,8 +10,8 @@ * governing permissions and limitations under the License. */ -import {chain} from '@react-aria/utils'; -import React, {RefObject, useCallback, useLayoutEffect, useRef} from 'react'; +import {chain, useLayoutEffect} from '@react-aria/utils'; +import React, {RefObject, useCallback, useRef} from 'react'; import {SpectrumTextFieldProps, TextFieldRef} from '@react-types/textfield'; import {TextFieldBase} from './TextFieldBase'; import {useControlledState} from '@react-stately/utils'; diff --git a/packages/@react-spectrum/utils/package.json b/packages/@react-spectrum/utils/package.json index 15f75c6cd33..bb7c91d2a9f 100644 --- a/packages/@react-spectrum/utils/package.json +++ b/packages/@react-spectrum/utils/package.json @@ -21,6 +21,7 @@ "dependencies": { "@babel/runtime": "^7.6.2", "@react-aria/i18n": "^3.1.0", + "@react-aria/ssr": "3.0.0-alpha.1", "@react-aria/utils": "^3.2.0", "@react-types/shared": "^3.2.0", "clsx": "^1.1.1" diff --git a/packages/@react-spectrum/utils/src/useHasChild.ts b/packages/@react-spectrum/utils/src/useHasChild.ts index 8e494fe7c81..efe487acef9 100644 --- a/packages/@react-spectrum/utils/src/useHasChild.ts +++ b/packages/@react-spectrum/utils/src/useHasChild.ts @@ -10,7 +10,8 @@ * governing permissions and limitations under the License. */ -import {RefObject, useLayoutEffect, useState} from 'react'; +import {RefObject, useState} from 'react'; +import {useLayoutEffect} from '@react-aria/utils'; export function useHasChild(query: string, ref: RefObject) { let [hasChild, setHasChild] = useState(true); diff --git a/packages/@react-spectrum/utils/src/useMediaQuery.ts b/packages/@react-spectrum/utils/src/useMediaQuery.ts index a65ef9f8464..a924e064980 100644 --- a/packages/@react-spectrum/utils/src/useMediaQuery.ts +++ b/packages/@react-spectrum/utils/src/useMediaQuery.ts @@ -11,6 +11,7 @@ */ import {useEffect, useState} from 'react'; +import {useIsSSR} from '@react-aria/ssr'; export function useMediaQuery(query: string) { let supportsMatchMedia = typeof window !== 'undefined' && typeof window.matchMedia === 'function'; @@ -36,5 +37,8 @@ export function useMediaQuery(query: string) { }; }, [supportsMatchMedia, query]); - return matches; + // If in SSR, the media query should never match. Once the page hydrates, + // this will update and the real value will be returned. + let isSSR = useIsSSR(); + return isSSR ? false : matches; } diff --git a/packages/@react-stately/radio/src/useRadioGroupState.ts b/packages/@react-stately/radio/src/useRadioGroupState.ts index c300bb5be5c..9cc4555e1a6 100644 --- a/packages/@react-stately/radio/src/useRadioGroupState.ts +++ b/packages/@react-stately/radio/src/useRadioGroupState.ts @@ -15,7 +15,11 @@ import {useControlledState} from '@react-stately/utils'; import {useMemo, useState} from 'react'; export interface RadioGroupState { - /** The name for the group, used for native form submission. */ + /** + * The name for the group, used for native form submission. + * @deprecated + * @private + */ readonly name: string, /** The currently selected value. */ @@ -39,6 +43,7 @@ let i = 0; * and manages selection and focus state. */ export function useRadioGroupState(props: RadioGroupProps): RadioGroupState { + // Preserved here for backward compatibility. React Aria now generates the name instead of stately. let name = useMemo(() => props.name || `radio-group-${instance}-${++i}`, [props.name]); let [selectedValue, setSelected] = useControlledState(props.value, props.defaultValue, props.onChange); let [lastFocusedValue, setLastFocusedValue] = useState(null); diff --git a/packages/@react-stately/virtualizer/package.json b/packages/@react-stately/virtualizer/package.json index 32155ab0d10..23803dcc594 100644 --- a/packages/@react-stately/virtualizer/package.json +++ b/packages/@react-stately/virtualizer/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@babel/runtime": "^7.6.2", + "@react-aria/utils": "3.2.0", "@react-types/shared": "^3.1.0" }, "peerDependencies": { diff --git a/packages/@react-stately/virtualizer/src/Virtualizer.ts b/packages/@react-stately/virtualizer/src/Virtualizer.ts index 4a3932f03b6..d848663d750 100644 --- a/packages/@react-stately/virtualizer/src/Virtualizer.ts +++ b/packages/@react-stately/virtualizer/src/Virtualizer.ts @@ -404,7 +404,7 @@ export class Virtualizer { */ relayout(context: InvalidationContext = {}) { // Ignore relayouts while animating the scroll position - if (this._scrollAnimation) { + if (this._scrollAnimation || typeof requestAnimationFrame === 'undefined') { return; } diff --git a/packages/@react-stately/virtualizer/src/useVirtualizerState.ts b/packages/@react-stately/virtualizer/src/useVirtualizerState.ts index 37125ea5591..bdfeed958d9 100644 --- a/packages/@react-stately/virtualizer/src/useVirtualizerState.ts +++ b/packages/@react-stately/virtualizer/src/useVirtualizerState.ts @@ -11,11 +11,12 @@ */ import {Collection} from '@react-types/shared'; -import {Key, useCallback, useEffect, useLayoutEffect, useMemo, useState} from 'react'; +import {Key, useCallback, useEffect, useMemo, useState} from 'react'; import {Layout} from './Layout'; import {Rect} from './Rect'; import {ReusableView} from './ReusableView'; import {Size} from './Size'; +import {useLayoutEffect} from '@react-aria/utils'; import {Virtualizer} from './Virtualizer'; interface VirtualizerProps {