Toast 기능 추가
바닐라 자바스크립트 -> react에서 Toast 적용하기
바닐라 자바스크립트에서도 Toast 메시지를 구현할 때, insertAdjacentElement, removeChild
등과 같은 메서드로 돔을 직접적으로 조작하여 구현하였다.
하지만 react에서는 직접적인 돔조작을 하지말라고 들었기 때문에 다른방식으로 구현해야 했다.
구현방식
토스트 메시지 목록을 전역 상태로 관리하여 추가 삭제를 하였고 div.root 요소를 건드리지 않고 body에 토스트 컨테이너를 생성한뒤 그 컨테이너에 createPortal을 사용하여 토스트 메시지들을 붙이는 방식을 이용했다.
keyword
- Portals
- useContext
body 하위에 toast-container 요소를 생성 후 하위에 토스트 메시지들을 관리
function CreateToastPortal({ children }: PortalProps) {
const container = document.getElementById('toast-container');
let newContainer;
if (!container) {
const toast = document.createElement('div');
toast.setAttribute('id', 'toast-container');
newContainer = toast;
document.body.appendChild(newContainer);
} else {
newContainer = container;
}
return ReactDOM.createPortal(children, newContainer);
}
useContext로 메시지 상태 관리
const [toasts, setToasts] = useState<ToastState[]>([]);
const createToast = useCallback((toast: Toast) => {
setToasts((prevToasts) => [...prevToasts, { id: nanoid(), ...toast }]);
}, []);
const hideToast = (toastId: string) => {
setToasts((prevToasts) => prevToasts.filter(({ id }) => id !== toastId));
};
<toastContext.Provider value={createToast}>
<CreateToastPortal>
{toasts.map((toast) => (
<ToastComponent key={toast.id} hideToast={hideToast} {...toast} />
))}
</CreateToastPortal>
{children}
</toastContext.Provider>
이제 사용하고 싶은 곳에서 적절하게 메시지 상태를 추가해주면 된다.
이전에 바닐라로 구현했던 방식
import './toast.scss';
class Toast {
constructor(props) {
this.timeout = props?.timeout || 3000;
this.type = props?.type || 'success';
this.content = props?.content || '성공';
this.render();
}
createDom(tagName, attrs) {
const $dom = document.createElement(tagName);
for (const [key, value] of Object.entries(attrs)) {
$dom[key] = value;
}
return $dom;
}
render() {
const container = document.querySelector('.toast-container');
let newContainer;
if (!container) {
newContainer = this.createDom('div', {
className: 'toast-container',
});
document.body.appendChild(newContainer);
} else {
newContainer = container;
}
this.toast = this.createDom('div', {
className: 'toast',
});
newContainer.insertAdjacentElement('beforeend', this.toast);
this.toast.classList.add(this.type);
// setting content
this.toast.appendChild(
this.createDom('h4', {
innerText: this.content,
className: 'toast-content',
}),
);
// setting timer
this.toastTimeout.call(this, this.timeout);
this.progress = this.createDom('div', {
className: 'toast-progress',
});
this.toast.insertAdjacentElement('beforeend', this.progress);
this.progress.style.animation = `toast_progress ${this.timeout}ms linear forwards`;
}
toastTimeout(time) {
setTimeout(() => {
this.toast.classList.add('is-hiding');
setTimeout(() => {
this.hide();
}, 1500);
}, time);
}
hide() {
this.toast.parentNode?.removeChild(this.toast);
}
}
export default Toast;
// 사용법
// 1. 사용할 컴포넌트에서 import
// import Toast from '../../components/Toast/Toast';
// 2. 알림을 사용하고 싶은 곳에서
// new Toast({ content: '입력하고 싶은 메시지'})
// new Toast({ timeout: 3000, content: 'test', type: 'success' })
// new Toast({ timeout: 3000, content: 'test', type: 'fail' })
// new Toast()
// default timeout:3000, content: 성공, type: success
Author And Source
이 문제에 관하여(Toast 기능 추가), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jiseong/Toast-기능-추가저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)