지난번 순수 javascript만으로 animation이 있는 drag and drop list를 만들려다가 잘 되지 않았다.
javascript로 직접 구현하려 했으나 애니메이션발동 도중 DOM을 변경하거나
(DOM 위치가 바뀌면 애니메이션이 바뀐 위치를 기준으로 동작하기 때문에 계산이 어렵다)
애니메이션이 끝나기 전에 애니메이션의 도착지점을 변경하는 것이 어려워서 라이브러리를 사용하기로 했다
라이브러리를 사용하기로 결정했고 이번에 라이브러리를 사용해서 구현한 내용을 정리해보려한다.
npm install --save react-sortablejs sortablejs
npm install --save-dev @types/sortablejs
force flag를 사용해서 설치를 했다.
// 사용 예 - 코드 1
const { createLectureData, lectureData, saveCourseInfoDone } = useSelector((state: RootState) => state.lecture);
const [whatYouCanLearn, setWhatYouCanLearn] = useState<ItemInterface[]>(
lectureData?.courseInfo.whatYouCanLearn.map((item) => ({
id: item.order,
name: item.name,
}))
);
<ReactSortable list={whatYouCanLearn} setList={setWhatYouCanLearn} animation={200} handle=".handle">
{whatYouCanLearn.map((item, index) => (
<TextListBox key={item.id} item={item} list={whatYouCanLearn} setList={setWhatYouCanLearn} index={index} />
))}
</ReactSortable>
// react-sortablejs에서 권장하는 item type interface - 코드 2
export interface ItemInterface {
/** The unique id associated with your item. It's recommended this is the same as the key prop for your list item. */
id: string | number;
/** When true, the item is selected using MultiDrag */
selected?: boolean;
/** When true, the item is deemed "chosen", which basically just a mousedown event. */
chosen?: boolean;
/** When true, it will not be possible to pick this item up in the list. */
filtered?: boolean;
[property: string]: any;
}
// 내가 사용하는 item의 type - 코드 3
export type LectureInfoChild = {
name: string;
order: number | string;
};
// react-sortablejs에 맞게 변형 - 코드 4
useEffect(() => {
setWhatYouCanLearn(
lectureData?.courseInfo.whatYouCanLearn.map((item) => ({
id: item.order,
name: item.name,
}))
);
}, [lectureData?.courseInfo]);
DO NOT use the index as a key for your list items. Sorting will not work.
In all the examples above, I used an object with an ID. You should do the same!
I may even enforce this into the design to eliminate errors.
타입스크립트를 사용하면서 타입이 맞지 않으면 일부로 타입을 맞추려는 코드들이 있다.(코드 4같은 경우)
타입을 맞추기 위한 이런 것이 잘 하고 있는 것인지 아닌지 스스로 판단하기 어려운 것 같다.
react-sortablejs에서 리스트 아이템의 전체가 아닌 특정 부분만 드래그를 가능하게 만들고 싶은 경우(핸들을 사용)
코드 1에서 ReactSortable컴포넌트의 handle=".handle" 속성
에서 클래스 이름을 지정 후
아이템 컴포넌트의 핸들 부분에 클래스 이름을 맞춰주면 된다.
<DynamicBox>
<div>{item.name}</div>
<div>
<button onClick={onClickDelete} type="button">
<DeleteIcon />
</button>
<DraggableButton className="handle"> // 핸들을 위한 클래스 이름 지정
<DragHandleIcon />
</DraggableButton>
</div>
</DynamicBox>