Skip to content

Commit

Permalink
fix console errors from bezier-react (#2238)
Browse files Browse the repository at this point in the history
<!--
  How to write a good PR title:
- Follow [the Conventional Commits
specification](https://www.conventionalcommits.org/en/v1.0.0/).
  - Give as much context as necessary and as little as possible
  - Prefix it with [WIP] while it’s a work in progress
-->

## Self Checklist

- [x] I wrote a PR title in **English** and added an appropriate
**label** to the PR.
- [x] I wrote the commit message in **English** and to follow [**the
Conventional Commits
specification**](https://www.conventionalcommits.org/en/v1.0.0/).
- [x] I [added the
**changeset**](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md)
about the changes that needed to be released. (or didn't have to)
- [x] I wrote or updated **documentation** related to the changes. (or
didn't have to)
- [x] I wrote or updated **tests** related to the changes. (or didn't
have to)
- [x] I tested the changes in various browsers. (or didn't have to)
  - Windows: Chrome, Edge, (Optional) Firefox
  - macOS: Chrome, Edge, Safari, (Optional) Firefox

## Related Issue

None

## Summary

bezier-react 를 사용할 때 뜨는 리액트 경고를 해결합니다.

## Details

### ToastContainer 내 `Each child in a list should have a unique "key"
prop.` 경고

![스크린샷 2024-05-24 22 38
43](https://github.com/channel-io/bezier-react/assets/42037851/86ac65cf-ce9c-4a2d-9e0d-1930efee0ea2)

이 이슈는 다음의 원인으로 인해 발생하는 이슈입니다.

`packages/bezier-react/src/components/Toast/Toast.tsx` 내
`createContainer` 함수에서 InvertedThemeProvider를 사용하게 되는데, 이때 해당 컴포넌트에
key값이 들어가지 않아 생기는 에러입니다.

이를 InvertedThemeProvider 내에 넣도록 변경함으로써, 문제를 해결하고자 합니다.

### BaseTagBadgeText 내 `forwardRef render functions accept exactly two
parameters: props and ref. Did you forget to use the ref parameter?` 에러

![스크린샷 2024-05-24 22 42
22](https://github.com/channel-io/bezier-react/assets/42037851/50064080-9ba0-4426-aa75-21931d9bd5ac)

이 이슈는 다음 원인으로 인해 발생됨이 추정됩니다.
stack trace를 따라가다보면, 최종 끝에 다다르는 파일이 `BaseTagBadge.mjs` 에 위치하게 되는데, 여기서
유일하게 ref parameter를 사용하지 않는 컴포넌트는 `BaseTagBadgeText` 컴포넌트가 유일합니다.

따라서 해당 컴포넌트에 forwardRef를 사용하게 함으로써 이 문제를 해결하고자 합니다.

### NextJS와 같은 SSR 환경에서 useLayoutEffect가 동작하지 않는 이슈

```
Warning: useLayoutEffect does nothing on the server, because its effect cannot be encoded into the server renderer's output format. This will lead to a mismatch between the initial, non-hydrated UI and the intended UI. To avoid this, useLayoutEffect should only be used in components that render exclusively on the client. See https://reactjs.org/link/uselayouteffect-ssr for common fixes.
```

https://reactjs.org/link/uselayouteffect-ssr 에 따르면, SSR 환경에서는
useLayoutEffect가 동작하지 않게 되고, 이는 곧 hydration mismatch로 이어지게 되어 해당 경고가
출력됨을 안내하고 있습니다.

여기서 제시하는 방법은 총 2가지로, 1. `useLayoutEffect` 를 `useEffect` 로 변경하거나, 2.
useLayoutEffect를 사용하는 컴포넌트를 Lazy하게 로딩하는 방법을 제시하고 있습니다.

하지만 위 2가지 방법은 각각 다음과 같은 문제가 있는데요.
- 1번의 경우 : useEffect를 사용하게 되어 처음 의도하였던 UX에서 벗어날 여지가 있습니다.
- 2번의 경우 : 컴포넌트를 Lazy하게 로딩하는 경우, SSR 환경에서 하위 컴포넌트가 로딩되지 않아 SEO에 부정적인 영향을
미치게 됩니다.

이 대신 서버사이드 렌더링 환경에서는 useEffect를 반환하고, 클라이언트사이드 환경에서는 useLayoutEffect를
사용하는 `useIsomorphicLayoutEffect` 을 만들어 이 문제를 해결하고자 합니다.
([#](https://medium.com/@alexandereardon/uselayouteffect-and-ssr-192986cdcf7a))

해당 방식은
[react-redux](https://github.com/reduxjs/react-redux/blob/d16262582b2eeb62c05313fca3eb59dc0b395955/src/components/connectAdvanced.js#L40)
와
[react-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/blob/master/src/view/use-isomorphic-layout-effect.js)
와 같이 유명한 라이브러리에서 사용되고 있어, 안정성이 보장된다고 판단됩니다.

### Breaking change? (Yes/No)

NO 

## References

- [useLayoutEffect and
SSR](https://medium.com/@alexandereardon/uselayouteffect-and-ssr-192986cdcf7a)

---------

Co-authored-by: Yang Wooseong (Andrew) <[email protected]>
  • Loading branch information
nabi-chan and yangwooseong authored May 29, 2024
1 parent 8136579 commit 1fa1309
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 12 deletions.
6 changes: 6 additions & 0 deletions .changeset/red-worms-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@channel.io/bezier-react": patch
---

- Fix ReactJS console warnings.
- Introduce `useIsomorphicLayoutEffect` hook to use `useLayoutEffect` in SSR environment.
5 changes: 3 additions & 2 deletions packages/bezier-react/src/components/AutoFocus/AutoFocus.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { forwardRef, useLayoutEffect, useState } from 'react'
import React, { forwardRef, useState } from 'react'

import { Slot } from '@radix-ui/react-slot'

import { useIsomorphicLayoutEffect } from '~/src/hooks/useIsomorphicLayoutEffect'
import useMergeRefs from '~/src/hooks/useMergeRefs'

import { type AutoFocusProps } from './AutoFocus.types'
Expand All @@ -27,7 +28,7 @@ export const AutoFocus = forwardRef<HTMLElement, AutoFocusProps>(
function AutoFocus({ children, when = true, ...rest }, forwardedRef) {
const [target, setTarget] = useState<HTMLElement | null>(null)

useLayoutEffect(
useIsomorphicLayoutEffect(
function focus() {
if (target && when) {
target.focus()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,11 @@ export const BaseTagBadge = forwardRef<HTMLDivElement, BaseTagBadgeProps>(
export const BaseTagBadgeText = forwardRef<
HTMLDivElement,
BaseTagBadgeTextProps
>(function BaseTagBadgeText({ size, children, ...rest }) {
>(function BaseTagBadgeText({ size, children, ...rest }, forwardedRef) {
return (
<Text
typo={getProperTypo(size)}
ref={forwardedRef}
{...rest}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { useLayoutEffect, useMemo, useState } from 'react'
import React, { useMemo, useState } from 'react'

import { useIsomorphicLayoutEffect } from '~/src/hooks/useIsomorphicLayoutEffect'
import { createContext } from '~/src/utils/react'
import { isEmpty } from '~/src/utils/type'

Expand Down Expand Up @@ -30,7 +31,7 @@ export function FeatureProvider({ children, features }: FeatureProviderProps) {
const [featureFlag, setFeatureFlag] =
useState<FeatureFlag>(initialFeatureFlag)

useLayoutEffect(
useIsomorphicLayoutEffect(
function activateFeatures() {
if (isEmpty(features)) {
return
Expand Down
6 changes: 3 additions & 3 deletions packages/bezier-react/src/components/Overlay/Overlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, {
forwardRef,
useCallback,
useEffect,
useLayoutEffect,
useReducer,
useRef,
useState,
Expand All @@ -12,6 +11,7 @@ import ReactDOM from 'react-dom'
import classNames from 'classnames'

import useEventHandler from '~/src/hooks/useEventHandler'
import { useIsomorphicLayoutEffect } from '~/src/hooks/useIsomorphicLayoutEffect'
import useMergeRefs from '~/src/hooks/useMergeRefs'

import { useModalContainerContext } from '~/src/components/Modal'
Expand Down Expand Up @@ -94,7 +94,7 @@ export const Overlay = forwardRef<HTMLDivElement, OverlayProps>(
}
}, [container, hasContainer])

useLayoutEffect(
useIsomorphicLayoutEffect(
function initContainerRect() {
if (show) {
handleContainerRect()
Expand Down Expand Up @@ -125,7 +125,7 @@ export const Overlay = forwardRef<HTMLDivElement, OverlayProps>(
}
}, [target])

useLayoutEffect(
useIsomorphicLayoutEffect(
function initTargetRect() {
if (show) {
handleTargetRect()
Expand Down
5 changes: 3 additions & 2 deletions packages/bezier-react/src/components/TextArea/TextArea.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { forwardRef, useLayoutEffect, useRef } from 'react'
import React, { forwardRef, useRef } from 'react'

import classNames from 'classnames'
import TextareaAutosize from 'react-textarea-autosize'

import { useIsomorphicLayoutEffect } from '~/src/hooks/useIsomorphicLayoutEffect'
import {
COMMON_IME_CONTROL_KEYS,
useKeyboardActionLockerWhileComposing,
Expand Down Expand Up @@ -43,7 +44,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
onKeyUp,
})

useLayoutEffect(function initialAutoFocus() {
useIsomorphicLayoutEffect(function initialAutoFocus() {
function setSelectionToEnd() {
inputRef.current?.setSelectionRange(
inputRef.current?.value.length,
Expand Down
3 changes: 1 addition & 2 deletions packages/bezier-react/src/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,8 @@ export function ToastProvider({

const createContainer = useCallback(
(placement: ToastPlacement, toasts: ToastType[]) => (
<InvertedThemeProvider>
<InvertedThemeProvider key={placement}>
<div
key={placement}
style={{
bottom: px(offset?.bottom ?? DEFAULT_OFFSET.bottom),
...(placement === 'bottom-right'
Expand Down
9 changes: 9 additions & 0 deletions packages/bezier-react/src/hooks/useIsomorphicLayoutEffect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useEffect, useLayoutEffect } from 'react'

/**
* @description This hook is used to handle the layout effect in a way that is compatible with SSR.
* In Server Environments, this hook will use `useEffect` instead of `useLayoutEffect`.
* @see https://react.dev/reference/react/useLayoutEffect#troubleshooting
*/
export const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect

0 comments on commit 1fa1309

Please sign in to comment.