Trello Clone

์œค์„œยท2023๋…„ 11์›” 12์ผ
0

[Nomad Coders] React JS Master Class

๋ชฉ๋ก ๋ณด๊ธฐ
5/5

๐Ÿ“š useSelector

Recoil์˜ Selector๋ž€ Recoil์—์„œ ์ œ๊ณตํ•˜๊ณ  ์žˆ๋Š” ์ˆœ์ˆ˜ํ•จ์ˆ˜์˜ ๊ฐœ๋…์ด๋‹ค. Selector๋Š” Derived State ์ฆ‰, ํŒŒ์ƒ๋œ State๋ฅผ ์ €์žฅํ•˜๋ฉฐ ๊ฐ€๊ณตํ•˜์—ฌ returnํ•ด ์ค„ ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜์ด๋‹ค. Selector๋ฅผ ์ด์šฉํ•˜์—ฌ atom์„ ์ ๊ฒŒ ๋งŒ๋“ค๊ณ  ๊ทธ ๊ฐ’์— ์ ‘๊ทผํ•˜๊ธฐ ์šฉ์ดํ•˜๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค! ๐Ÿ˜

โค๏ธโ€๐Ÿ”ฅ ์ตœ์†Œํ•œ์˜ ์ƒํƒœ ์ง‘ํ•ฉ๋งŒ atom์— ์ €์žฅํ•˜๊ณ  ๋‹ค๋ฅธ ๋ชจ๋“  ํŒŒ์ƒ๋˜๋Š” ๋ฐ์ดํ„ฐ๋Š” Selector์— ๋ช…์‹œํ•œ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ํšจ์œจ์ ์œผ๋กœ ๊ณ„์‚ฐํ•จ์œผ๋กœ์จ ์“ธ๋ชจ์—†๋Š” ์ƒํƒœ์˜ ๋ณด์กด์„ ๋ฐฉ์ง€ํ•œ๋‹ค.

import { selector } from "recoil";

๐Ÿคญ Get ํ•จ์ˆ˜ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

atom.ts

export const minuteState = atom({
  key: "minutes",
  default: 0,
});

export const hourSelector = selector({
  key: "hours",
  get: ({ get }) => {
    const minutes = get(minuteState);
    return minutes / 60;
  },
});

๐Ÿ‘‰๐Ÿป Selector๋Š” get ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด ๋‹ค์–‘ํ•œ atom์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

App.tsx

 const [minutes, setMinutes] = useRecoilState(minuteState);
 const hours = useRecoilValue(hourSelector);
 const onMinutesChange = (event: React.FormEvent<HTMLInputElement>) => {
    setMinutes(+event.currentTarget.value);
  };
  return (
    <div>
      <input
        value={minutes}
        onChange={onMinutesChange}
        type="number"
        placeholder="Minutes"
      />
      <input value={hours} type="number" placeholder="Hours" />
    </div>
  );

๐Ÿ‘‰๐Ÿป Selector๊ฐ€ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’์„ ๋‹ค๋ฃจ๊ณ  ์‹ถ๋‹ค๋ฉด useRecoilValue์˜ ์ธ์ž์— Selector ์ด๋ฆ„์„ ๋„ฃ์–ด ์‚ฌ์šฉํ•œ๋‹ค.
โœ… ๊ฐ’์„ ์ฝ๊ธฐ ์ „์šฉ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด useRecoilValue, set ํ•จ์ˆ˜๋„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด useRecoilState๋ฅผ ์ด์šฉํ•œ๋‹ค.

๐Ÿซข Set ํ•จ์ˆ˜ ์‚ฌ์šฉํ•ด ๋ณด๊ธฐ

atom.ts

export const hourSelector = selector<number>({
  key: "hours",
  get: ({ get }) => {
    const minutes = get(minuteState);
    return minutes / 60;
  },
  set: ({ set }, newValue) => {
    const minutes = Number(newValue) * 60;
    set(minuteState, minutes);
  },
});

๐Ÿ‘‰๐Ÿป set ํ•จ์ˆ˜๋Š” ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ ๋ณ€๊ฒฝ๋  ๊ฐ’์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ž…๋ ฅํ•œ ๊ฐ’์„ ํ•จ์ˆ˜ ๋‚ด์—์„œ ๋ณ€๊ฒฝํ•˜์—ฌ ๋‹ค๋ฅธ ๊ฐ’์œผ๋กœ ๋งŒ๋“ค์–ด ์ค„ ์ˆ˜ ์žˆ๋‹ค.

App.tsx

const [minutes, setMinutes] = useRecoilState(minuteState);
const [hours, setHours] = useRecoilState(hourSelector);
const onMinutesChange = (event: React.FormEvent<HTMLInputElement>) => {
    setMinutes(+event.currentTarget.value);
  };
  const onHoursChange = (event: React.FormEvent<HTMLInputElement>) => {
    setHours(+event.currentTarget.value);
  };
  return (
    <div>
      <input
        type="number"
        placeholder="Minutes"
      />
      <input
        onChange={onHoursChange}
        value={hours}
        type="number"
        placeholder="Hours"
      />
    </div>
  );

๐Ÿ‘‰๐Ÿป ์ด์ œ ์‹œ๊ฐ„์— ๋Œ€ํ•œ input ํƒœ๊ทธ์—์„œ ๊ฐ’์„ ๋ฐ›์•„์„œ, hourSelector๋กœ ๋„˜๊ฒจ minuteState ๊ฐ’์œผ๋กœ ๋ณ€ํ™˜์‹œ์ผœ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค. ๊ฐ๊ฐ์˜ ์‹œ๊ฐ„๊ณผ ๋ถ„์˜ onChange ํ•จ์ˆ˜๋กœ ๊ฐ’์˜ ๋ณ€ํ™”๋ฅผ ๊ด€์ฐฐํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

๐Ÿฅ react-beautiful-dnd

ํ™”๋ฉด์˜ ์š”์†Œ๋ฅผ ๋“œ๋ž˜๊ทธํ•˜๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜๋Š” ์œ„์น˜์— ๋“œ๋žํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์†์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

Droppable์€ ๋“œ๋ž˜๊ทธ & ๋“œ๋ž์ด ๊ฐ€๋Šฅํ•œ ๋ถ€๋ถ„์„ ๋œปํ•˜๊ณ , Draggable์€ ๋“œ๋ž˜๊ทธ & ๋“œ๋ž์ด ๊ฐ€๋Šฅํ•œ ์š”์†Œ๋ฅผ ๋œปํ•œ๋‹ค. ์ „์ž์˜ ์ž์‹ ์š”์†Œ๋กœ ํ›„์ž๊ฐ€ ๋“ค์–ด๊ฐ€ ์žˆ์–ด์•ผ ํ•จ! ๐Ÿ”ฅ

App.tsx

const toDos = ["a", "b", "c", "d", "e", "f"];

function App() {
  const onDragEnd = () => {};
  return (
    <DragDropContext onDragEnd={onDragEnd}>
      <Wrapper>
        <Boards>
          <Droppable droppableId="one">
            {(magic) => (
              <Board ref={magic.innerRef} {...magic.droppableProps}>
                {toDos.map((toDo, index) => (
                  <Draggable draggableId={toDo} index={index}>
                    {(magic) => (
                      <Card
                        ref={magic.innerRef}
                        {...magic.dragHandleProps}
                        {...magic.draggableProps}
                      >
                        {toDo}
                      </Card>
                    )}
                  </Draggable>
                ))}
                {magic.placeholder}
              </Board>
            )}
          </Droppable>
        </Boards>
      </Wrapper>
    </DragDropContext>
  );
}

๐Ÿ‘‰๐Ÿป DragDropContext์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋“œ๋ž˜๊ทธ์™€ ๋“œ๋ž์ด ๋๋‚ฌ์„ ๋•Œ ์ˆ˜ํ–‰๋  onDragEnd ํ•จ์ˆ˜๋ฅผ ๊ผญ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ์ž์‹ ์š”์†Œ๋“ค์ด ํƒ€ ์ผ๋ฐ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ๋‹ค๋ฅด๊ฒŒ ํ•จ์ˆ˜ ํ˜•ํƒœ๋กœ ๊ฐ์‹ธ์ ธ ์žˆ๋‹ค.

๐Ÿ‘€ Draggable ์š”์†Œ๋“ค์˜ Reordering

๋“œ๋ž˜๊ทธ์™€ ๋“œ๋ž์ด ๋๋‚ฌ์„ ๋•Œ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜๋กœ์จ ๊ธฐ์กด์˜ ๋ฐฐ์—ด ํ˜•ํƒœ๋ฅผ ๋ฐ”๊ฟ” ์ฃผ์ง€ ์•Š์œผ๋ฉด ์›๋ž˜ ํ˜•ํƒœ ๊ทธ๋Œ€๋กœ map ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰์‹œํ‚ค๊ธฐ ๋•Œ๋ฌธ์— ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ์ง€ ์•Š์Œ!! ๐Ÿ™…๐Ÿปโ€โ™€๏ธ ๋”ฐ๋ผ์„œ onDragEnd ํ•จ์ˆ˜ ๋‚ด์—์„œ ๊ธฐ์กด์˜ ๋ฐฐ์—ด์„ ๊ผญ ๋ณ€ํ˜•์‹œ์ผœ ์ฃผ์–ด์•ผ ํ•จ.

const onDragEnd = ({ draggableId, destination, source }: DropResult) => {
    if (!destination) return;
    setToDos((oldToDos) => {
      const toDosCopy = [...oldToDos];
      // 1) Delete item on source.index
      console.log("Delete item on", source.index);
      console.log(toDosCopy);
      toDosCopy.splice(source.index, 1);
      console.log("Deleted item");
      console.log(toDosCopy);
      // 2) Put back the item on the destination.index
      console.log("Put back", draggableId, "on ", destination.index);
      toDosCopy.splice(destination?.index, 0, draggableId);
      console.log(toDosCopy);
      return toDosCopy;
    });
  };

๐Ÿ‘‰๐Ÿป ๋“œ๋ž˜๊ทธ์™€ ๋“œ๋ž์ด ๋๋‚ฌ์„ ๋•Œ ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ฒด๋กœ ๋ฐ›์•„์˜ค๊ธฐ ๋•Œ๋ฌธ์— ์ธ์ž๋กœ ๋“œ๋ž˜๊ทธ ๋œ ์š”์†Œ์˜ ์•„์ด๋””์™€ ์‹œ์ž‘์ , ๋„์ฐฉ์ ์„ ๋„ฃ์–ด์ค€๋‹ค. ๋งŒ์•ฝ ์‹œ์ž‘์  ๊ทธ๋Œ€๋กœ์˜ ์œ„์น˜์—์„œ ๋“œ๋ž˜๊ทธ์™€ ๋“œ๋ž์ด ๋๋‚ฌ๋‹ค๋ฉด, destination์ด ์กด์žฌํ•˜์ง€ ์•Š์„ ๊ฒƒ์ด๋ฏ€๋กœ ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ return ์‹œ์ผœ์ค€๋‹ค.
โœ… ๊ธฐ์กด์˜ ๋ฐฐ์—ด์„ ๊ฐ€์ ธ์™€ ์›€์ง์ธ ์š”์†Œ๋ฅผ ์‚ญ์ œ โžก๏ธ ๋„์ฐฉ์ ์˜ index์— ์š”์†Œ ์‚ฝ์ž… โžก๏ธ setToDos๋ฅผ ํ†ตํ•ด ์ƒˆ๋กœ์šด ๋ฐฐ์—ด ์ €์žฅ

profile
๋‚˜์˜ ์กฐ๊ฐ๋ฐฐ

0๊ฐœ์˜ ๋Œ“๊ธ€