forwardRef

고민의 시작은 styled-component로 만들어진 태그에 ref를 걸고 싶었다...
useRef를 이용해 기존에 하던 방식대로 해보았으나 당연히 안됨. styled-component의 진짜 태그는 style.js 안에 모여있기 때문일 것이라고 생각했다.
그럼 ref를 props 보내듯이 보내줘야 하는데....
그럴때 사용하는것이 forwardRef!!

// Modal.js
...
const attentionInput = useRef();
...

<Input
  ref={attentionInput}
  ...
/>
// style.js
const Input = forwardRef((props, ref) => {
  ...

  return <SearchInput ref={ref} />;
});

위 코드를 보면 html 엘리먼트에 forwardRef를 사용해 prop을 받아오고 있다.
prop 받아오는 형태가 조금 특이한데 'HTML 엘리먼트 접근이라는 특수한 용도로 사용되기 때문에 일반적인 prop으로 사용을 할 수 없다' 고 한다.

HTML 엘리먼트가 아닌 React 컴포넌트에서 ref prop을 사용하려면 React에서 제공하는 forwardRef()라는 함수를 사용해야 합니다. React 컴포넌트를 forwardRef()라는 함수로 감싸주면, 해당 컴포넌트는 함수는 두 번째 매개 변수를 갖게 되는데, 이를 통해 외부에서 ref prop을 넘길 수 있습니다.

라고 설명되어 있다.

drag and drop

각 종목의 순서를 바꿔줘야 하는데 디자이너 분이 제안한 방식은 드래그 앤 드랍이었다.
라이브러리를 찾던 중 'react-dnd' 를 적용해보았는데 너무 활용하기가 어려웠다...
이름도 아름다운 'react-beautiful-dnd' 라이브러리를 두번째로 적용해보았는데 굉장히 쉽고 빠르게 적용할 수 있었다.

// FormExerciseDnd.js

import React, { useState } from 'react';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
import { useDispatch, useSelector } from 'react-redux';
import styled from 'styled-components';
import { Text, Image } from '../shared/Styles';
import DragIndicator from '../img/drag-indicator.svg';
import { actionCreators as exerciseCreator } from '../redux/modules/exercise';

export default function FormExerciseDnd() {
  const dispatch = useDispatch();
  const lists = useSelector((state) => state.exercise.routine.myExercise);
  const handlelists = (result) => {
    // 드래그 앤 드랍 동작 로직 함수 시작
    // result에는 드래그의 시작 idx와 종료 idx 등 드래그 관련 여러 정보가 담겨있음.
    if (!result.destination) return;
    const items = [...lists];
    const [reorderedItem] = items.splice(result.source.index, 1);
    items.splice(result.destination.index, 0, reorderedItem);
    // 드래그 앤 드랍 동작 로직 함수 끝

    // 위 드래그 앤 드랍 로직 함수로는 드랍 이후 상태가 저장이 안됨
    // 당연한 얘기지만 새롭게 상태 저장을 하지 않았으므로, lists에 저장된 순서대로 다시 돌아감...
    // 따라서 아래처럼 순서를 새롭게 변경해서 redux에 저장해줘야 함.
    dispatch(exerciseCreator.reArrangeMyExercise(items));
  };

  return (
    // onDragEnd에 드래그 앤 드랍 로직 함수를 넣어야 함.
    <DragDropContext onDragEnd={handlelists}>
      <Droppable droppableId="lists">
        {(provided) => (
          <Container
            className="lists"
            {...provided.droppableProps}
            ref={provided.innerRef}
          >
            {lists.map((list, listIdx) => (
              <Draggable
                key={listIdx}
                // draggableId에는 string 값이 들어가야 함.
                draggableId={listIdx.toString()}
                index={listIdx}
                disableInteractiveElementBlocking="true"
              >
                {(provided) => (
                  <List
                    ref={provided.innerRef}
                    {...provided.dragHandleProps}
                    {...provided.draggableProps}
                    id={listIdx}
                  >
                    
                    ...
                    
                  </List>
                )}
              </Draggable>
            ))}

            {/* 가장 위로 위치를 변경할때 필요한 높이를 확보해준다고 한다. */}
            {provided.placeholder}
          </Container>
        )}
      </Droppable>
    </DragDropContext>
  );
}

...
profile
빠굥

0개의 댓글