falsy
버튼에 로딩 스피너를 추가
버튼에 로딩 스피너를 추가하는 방법입니다. button은 다양한 너비를 갖을 수 있고 또 입력한 텍스트 길이만큼 고정되어야 합니다. 로딩인 상황에 button의 너비가 줄어드는 일도 막아야 합니다.
import { ButtonWrapper, TextWrapper, LoaderWrapper } from './Button.style';
import { PulseLoader } from 'react-spinners';
type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & {
children: React.ReactNode;
onClick?: React.MouseEventHandler<HTMLButtonElement>;
isLoading?: boolean;
};
/** loader는 텍스트 너비가 필요해서 2개의 VisibilityWrapper 활용 */
export function Button({
children,
onClick,
isLoading = false,
...other
}: ButtonProps) {
return (
<ButtonWrapper
onClick={onClick}
disabled={isLoading}
isLoading={isLoading}
{...other}
>
<TextWrapper isLoading={isLoading}>{children}</TextWrapper>
// highlight-start
{isLoading && (
<LoaderWrapper>
<PulseLoader
color="#ffffff"
loading
margin={4}
size={12}
speedMultiplier={0.5}
/>
</LoaderWrapper>
)}
// highlight-end
</ButtonWrapper>
);
}
전략을 바꿨습니다. loading은 조건부 랜더링을 계속 처리합니다. 하지만 로딩하는 동안 텍스트도 조건부 스타일링으로 안보이게 만듭니다.
존재하지만 가시적으로 보이지 않게 때문에 텍스트 크기만큼 영역을 확보합니다.
import styled from '@emotion/styled';
export const ButtonWrapper = styled.button<{ isLoading: boolean }>`
all: unset;
${(props) => props.theme.fonts.body16Regular}
border-radius: 0.5rem;
border: none;
/* disabled 이면 gray가 되고 loading이면 green을 유지 */
background-color: ${(props) =>
props.disabled && !props.isLoading
? props.theme.colors.gray400
: props.theme.colors.green};
color: ${(props) => props.theme.colors.white};
height: 2.75rem;
position: relative;
width: fit-content;
min-width: 5.25rem;
display: flex;
align-items: center;
justify-content: center;
`;
export const TextWrapper = styled.span<{ isLoading: boolean }>`
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
color: ${(props) =>
// highlight-start
props.isLoading
? 'transparent'
: props.theme.colors.white}; /* 로딩하는 동안 text를 숨김 */
// highlight-end
margin: 0 1rem;
`;
export const LoaderWrapper = styled.div`
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
`;
이렇게 되면 레이아웃 쉬프트 없이 버튼에 로딩 상태를 보여줄 수 있습니다.