현재 진행하고 있는 프로젝트에서 DND 기능을 다시 만들어볼 기회가 생겼는데, 계속해서 DroppableId를 찾지 못하는 에러가 발생했다.
TR&DR
requestAnimationFrame을 불러 paint 이후에 Droppable을 등록하면 StrictMode를 해제하지 않아도 사용할 수 있다.
문제 자체는 쉽게 찾을 수가 있었는데,
현재 프로젝트의 리액트 버전 18의 StrictMode가 문제를 일으킨다는 것.
react 18의 StrictMode에서는 이전 버전들과 다르게 디버깅 목적을 위해 라이프사이클을 의도적으로 두 번 실행시키는데, 그 부분에서 등록된 Droppable의 ref 자체가 사라지는 이슈가 생겼던 것이었다.
* React mounts the component.
* Layout effects are created.
* Effects are created.
With Strict Mode in React 18, React will simulate unmounting and remounting the component in development mode:
* React mounts the component.
* Layout effects are created.
* Effects are created.
* React simulates unmounting the component.
* Layout effects are destroyed.
* Effects are destroyed.
* React simulates mounting the component with the previous state.
* Layout effects are created.
* Effects are created.
See docs for ensuring reusable state here.
React Docs에서는 version 18.의 StrictMode를 이렇게 설명하고 있다.
이후 다시 useLayoutEffect와 useEffect를 실행하는데, 이때 사용하는 값은 1.에서와 동일한데, 이때 참조해야할 ref의 데이터가 2번을 거치면서 사라져서 DroppableId 자체를 찾을 수 없었던 것.
// use-droppable-publisher.js l.262
// update is enabled with the marshal
// only need to update when there is a drag
useLayoutEffect(() => {
if (!whileDraggingRef.current) {
return;
}
marshal.updateDroppableIsEnabled(
publishedDescriptorRef.current.id,
!args.isDropDisabled,
);
}, [args.isDropDisabled, marshal]);
위 beatiful-dnd의 소스를 본다면, 1. useLayoutEffect의 내용에 Droppable을 등록하지만,
StrictMode의 2를 거치면서 ref의 참조가 사라짐 -> Droppable Id를 찾을 수 없음이 되는 것으로 보인다.
https://github.com/facebook/react/issues/24670
의 페이지에서도 같은 이슈를 찾을 수 있었는데, StrictMode에서 unmount / remount를 반복하는 과정에서 ref값이 사라지는 이슈이다.
첫 참조 페이지에서의 해결책은 다음과 같다.
layoutEffect로 등록 -> unmount / remount 시 ref값이 사라지기 때문에,
해당 unmount / remount 이후에 Drappable 자체를 등록하면 문제 없이 쓸수 있다.
따라서 requestAnimationFrame을 불러 paint 이후에 Droppable을 등록하면 StrictMode를 해제하지 않아도 사용할 수 있다.
하지만 다음에 Dnd기능을 요구받으면 다른 라이브러리를 사용하면 더 편할 것 같다.
https://dndkit.com/ 같은것들...
참조:
https://github.com/atlassian/react-beautiful-dnd/issues/2399
react-beatiful-dnd github
정말 감사합니다.... 위 문제때문에 라이브러리 없이 구현해야 하나 고민하고 있었는데 삽질이었군요