본문 바로가기

코딩/Today I Learn

React Js Master #9 selector set, beautiful-dnd

728x90

string 형태 number로 변경하기 

   

setMinutes(+ event.currentTarget.value);

   +"1"

 

Selector의 다른 속성 set

state를 set하는 걸 도와주는 속성

atom을 수정하는 것을 도와줌

 

 

const [] = useRecoilState는 배열을 받는데 

selector를 useRecoilState로 받으면  

첫번 째 array는 get property 에서 return 한 값.

두번 째는 set property를 부르는 함수 .

 

import { atom, selector } from "recoil";

export const minutesState = atom({
    key: "minnutes",
    default: 0,
});

export const hourSelector = selector<number>({ //atom을 각각 만들지않고mintues만 만들어서 값을 수정했다.

두개의 atom을 만들고 싶지 않다면 selector을 만들어서 수정하면됨
    key: "hours",
    get: ({ get }) => { //selector값을 어떻게 바꿀지 
        const min = get(minutesState);
        return min / 60;
    },
    set: ({ set }, newValue) => {  //atom을 수정하기 위해 selector을 어떻게 사용하는지
        const minutes = Number(newValue) * 60;
        set(minutesState, minutes);
    },
});

set? - 이 속성이 설정되면 selector는 쓰기 가능한 상태를 반환한다.


react-beautiful-dnd
React로 list를 만들기 위한 아름답고 접근 가능한 드래그 앤 드롭

 

https://www.npmjs.com/package/react-beautiful-dnd

 

react-beautiful-dnd

Beautiful and accessible drag and drop for lists with React. Latest version: 13.1.0, last published: a year ago. Start using react-beautiful-dnd in your project by running `npm i react-beautiful-dnd`. There are 1286 other projects in the npm registry using

www.npmjs.com

필수 prop인 onDragEnd가 필요하다고함

드래그가 끝날 때 불려지는 함수 

children이 필요하다고함

하위요소 span이든지, div든지 채워주면 애러가 사라짐 

위와같이 하면서 

 

첫번 째 argument provided 

 

  return (
        <DragDropContext onDragEnd={onDragEnd}>
            <div>
                <Droppable droppableId="one">
                    {(magic) => (
                        <ul ref={magic.innerRef} {...magic.droppableProps}>
                            <Draggable draggableId="first" index={0}>
                                {(magic) => (
                                    <li
                                        ref={magic.innerRef}
                                        {...magic.draggableProps}
                                        {...magic.dragHandleProps}
                                    >
                                        one
                                    </li>
                                )}
                            </Draggable>
                            <Draggable draggableId="second" index={1}>
                                {(magic) => (
                                    <li
                                        ref={magic.innerRef}
                                        {...magic.draggableProps}
                                        {...magic.dragHandleProps}
                                    >
                                        two
                                    </li>
                                )}
                            </Draggable>
                        </ul>
                    )}
                </Droppable>
            </div>
        </DragDropContext>
    );

 

dragHandle 드래그 하는 손잡이 

아무곳이나 누르면 움직이는 것이아니라 

움직이는 손잡이만 따로 만들기 

 

     {(magic) => (
                                    <li
                                        ref={magic.innerRef}
                                        {...magic.draggableProps}
                                    >
                                        <span {...magic.dragHandleProps}>  드래그 핸들프롭으 다른 곳에 위임함 이걸로만 드래그가능
                                            🔥
                                        </span>
                                        one
                                    </li>
                                )}

magic.placeholder

 

provided.placeholder (?ReactElement)
Draggable 엘리먼트를 드래그하는 동안 position: fixed(영역을 고정시킴)를 적용합니다.
Draggable을 드래그할 때 Droppable 리스트가 작아지는 것을 방지하기 위해 필요합니다.
Draggable 노드의 형제로 렌더링하는 것이 좋습니다.

provided
https://github.com/atlassian/react-beautiful-dnd/blob/HEAD/docs/api/droppable.md#1-provided-droppableprovided

    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.draggableProps}
                                                {...magic.dragHandleProps}
                                            >
                                                {toDo}
                                            </Card>
                                        )}
                                    </Draggable>
                                ))}
                                {magic.placeholder}
                            </Board>
                        )}
                    </Droppable>
                </Boards>
            </Wrapper>
        </DragDropContext>

 

 

아이템 드롭했을 때 재정렬하기

onDragEnd는 어떤 일이 일어났는지에 대한 정보로 많은 argument를줌 

onDragEnd=(args:any)=>{console.log(args)};

source와 destination을 볼 수 있음.

destination과 source이용하려니까 typescript가 안맞다고 불평한다.

onDRagEnd를 눌러서 type 설명을보면 DropResult안에 destination 을 볼 수 있으니 

({destination,source}:DropResult)로 타입 지정

 

array.splice 는 해당 배열을 mutate변화 시키는 동작이다.

atom  state세계에서는 mutate하면 안돼

새로운 배열을 만들어서 수정.

 

 

draggableId

+

draggableId에 toDo 내용을 넣었었음.

주의점!

보통 react에서 key는 index를 입력하는데 

beautiful dnd에서는 draggableId와 key내용이 같아야한다.

버그가 날 수 있으니 이 점 주의

draggableId와 key 모두 toDo인 것 확인

< Draggable /> list의 키
< Draggable /> list를 렌더링하는 경우 각 < Draggable />에 key prop을 추가하는 것이 중요합니다.

규칙
key는 list 내에서 고유해야 합니다.
key에 item의 index가 포함되어서는 안 됩니다. (map의 index사용 X)
일반적으로 draggableId를 key로 사용하면 됩니다.
주의! list에 key가 없으면 React가 경고하지만 index를 key로 사용하는 경우 경고하지 않습니다.
key를 올바르게 사용하지 않으면 정말 안 좋은 일이 생길 수 있습니다 💥
```
return items.map((item, index) => (
< Draggable
// adding a key is important!
key={item.id}
draggableId={item.id}
index={index}
>
나머지 코드..
```
https://github.com/atlassian/react-beautiful-dnd/blob/HEAD/docs/api/draggable.md#keys-for-a-list-of-draggable-

 

GitHub - atlassian/react-beautiful-dnd: Beautiful and accessible drag and drop for lists with React

Beautiful and accessible drag and drop for lists with React - GitHub - atlassian/react-beautiful-dnd: Beautiful and accessible drag and drop for lists with React

github.com

 

재정렬 할 때 shaking 하는 현상 고치기

재정렬할때 [a,b,c,d,e,f] 중에 f->e로 움직이면 

a부터 모든 요소들이 재정렬이된다. 

이유는 react특성상 state가 바뀌면 다시 렌더링 되기때문에 

부모가 바뀌면 자식이 바뀌고 그 자식, 자식도 다 바뀌는게 react

 

--> React memo

reat.js한테 prop이 변하지 않는다면 제발 이 components는 렌더링 하지 말라고 하는 역할을 함.

draggableCard에서 export default draagbleCard가 아니고

export default React.memo(DraggableCard);로 prop이 변하지 않았다면 draggableCard를 다시 렌더링하지 말라고 말함.

React.memo

React.memo는 고차 컴포넌트(Higher Order Component)입니다.
컴포넌트가 동일한 props로 동일한 결과를 렌더링해낸다면, React.memo를 호출하고 결과를 메모이징(Memoizing)하도록 래핑하여 경우에 따라 성능 향상을 누릴 수 있습니다. 즉, React는 컴포넌트를 렌더링하지 않고 마지막으로 렌더링된 결과를 재사용합니다.

React.memo는 props 변화에만 영향을 줍니다. React.memo로 감싸진 함수 컴포넌트 구현에 useState, useReducer 또는 useContext 훅을 사용한다면, 여전히 state나 context가 변할 때 다시 렌더링됩니다.
이 메서드는 오직 성능 최적화를 위하여 사용됩니다. 렌더링을 “방지”하기 위하여 사용하지 마세요. 버그를 만들 수 있습니다.

DraggableCard에게 동일한 index와 동일한 todo prop을 받으면 리랜더링을 하지 않도록 하기 위함이다.
```
function MyComponent(props) {
/* props를 사용하여 렌더링 */
}

export default React.memo(MyComponent, areEqual);
```
https://ko.reactjs.org/docs/react-api.html#reactmemo

 

React 최상위 API – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

todo,doing,done세가지 보드로 나누기

 

세가지 보드로 나누기위하여 기존["a","b","c","d","e"];배열 형식에서 

{ } object 형식으로 변경.

 

이 전에 사용했던 toDos.map()은 array에서만 사용가능 

obejct loop처리하기

 

const toDos = {

 x:["a","b"],

 y:["c","d"]

}

 

object.keys(toDos) // ["x","y"] --.> boardID로 사용하면되겠다. 아래 참고

toDos["x"] // ["a","b"]

 

object.keys(toDos).map(boardId)-->toDos[boardId])

                ["x","y"]                                   ["a","b"], ["c","d"]

===toDos["x"] , toDos["y"] / 같은결과

 

function Board({toDos, boardId}:IBoardProps) {
    return (
        <Droppable droppableId={boardId}> //
            {(magic) => (
                <Wrapper ref={magic.innerRef} {...magic.droppableProps}>
                    {toDos.map((toDo, index) => (
                        <DraggableCard key={toDo} index={index} toDo={toDo} />
                    ))}
                    {magic.placeholder}
                </Wrapper>
            )}
        </Droppable>
    );

oltypescript설정 value가 string[]인건 알지만 key가 string 인걸 알지못해. interface설정 함.

같은 보드 이동

oldToDos는 IToDoState를 보년 더 이상 array 구조가 아닌 object

key값에 변수값을 넣으려면 대괄호[]를 사용해야한다.

[source.droppableId ] == ["doing"]

boardCopy should be an array.const boardCopy = oldToDossource.droppableId is not the same as: const boardCopy = [...oldToDossource.droppableId] 배열이여야지 Array.splice() 적용가능 

마지막에 setTodos 함수에 인자로 디스트럭쳐링으로 oldTodos를 넣고 바뀐 board 프로퍼티를 더 넣어주면,
객체 안에서 키 값이 중복된 프로퍼티는 마지막에 선언된 프로퍼티를 사용하기때문에 저렇게 넣어줘도 상관없는것이다.

 

다른 보드끼리 이동 

보드끼리 이동 문제 없으나 

모든걸 다 옮겼을 때 

받는 역할인 div , 즉 receiver가 매우 영역이 좁아져있어서 최대한 제목까지 올려야만 이동이 된다.

댓글에 있는 

const onDragEnd = ({ draggableId, destination, source }: DropResult) => {
if (!destination) return;
setToDos((allBoards) => {
const copyToDos: IToDoState = {};
Object.keys(allBoards).forEach((toDosKey) => {
copyToDos[toDosKey] = [...allBoards[toDosKey]];
});
copyToDos[source.droppableId].splice(source.index, 1);
copyToDos[destination.droppableId].splice(
destination.index,
0,
draggableId
);
return copyToDos;
});
};
같은 보드안에서 이동, 다른 보드끼리 이동 한번에 처리가능합니다!

 

부모 Wrapper에 flex와 flex-direction을 주고 자식인 div-->Area로 flex-grow:! 주니 부모만큼 영역이 커짐

droppable 또 다른 인자 snapshot

인자 snapshot 오른쪽 마우스 눌러 type설명을 보면어떤 기능들이 있는지 알 수 있다.

Area는 단순 styled된 div이기때문에 prop인식하지 못해서 styled에 type 설명해주기

isDraggingOver인식

interface IAreaProps { //두 개라 길어져서 interface로 만듬
    isDraggingOver: boolean;
    isDraggingFromThis: boolean;
}
const Area = styled.div<IAreaProps>`
    background-color: ${(prop) =>
        prop.isDraggingOver
            ? "pink"
            : prop.isDraggingFromThis
            ? "red"
            : "blue"};
    flex-grow: 1;
    transition: background-color 0.3s ease-in-out;
`;

function Board({ toDos, boardId }: IBoardProps) {
    return (
        <Wrapper>
            <Title>{boardId}</Title>
            <Droppable droppableId={boardId}>
                {(magic, snapshot) => (
                    <Area
                        isDraggingOver={snapshot.isDraggingOver}
                        isDraggingFromThis={Boolean( //편의를 위해 이름을 바꿈
                            snapshot.draggingFromThisWith
                        )}
                        ref={magic.innerRef}
                        {...magic.droppableProps}
                    >

스타일 변경

https://flatuicolors.com/palette/us

 

Flat UI Colors 2 - 14 Color Palettes, 280 colors 🎨

280 handpicked colors ready for COPY & PASTE

flatuicolors.com

https://github.com/UHyun-K/trello-clone/commit/475621c9de6f3043976b5a5b0ba4ee485fda8e8c

 

style · UHyun-K/trello-clone@475621c

Show file tree Hide file tree Showing 2 changed files with 18 additions and 10 deletions.

github.com