Recoil 이해하기

이말감·2022년 5월 22일
0

Recoil 이해하기

원티드 프리온보딩 세 번째 팀 과제를 받았다 ~~~
리코일은 저번 과제인 영화 검색 앱을 만들 때 사용했지만.. 그때 당시 많이 공부를 못한 상태에서 바로 들어갔기 때문에 원활한 진행을 위해 공부를 해보려고 한다.
화이팅 ~~
참고 : Recoil 공식 문서

이 글은 Recoil 공식 문서를 보고 따라치면서 이해하는 글이라.. 별로입니다. 위의 링크로 가시는 것을 권장드립니다.

Recoil 시작하기

RecoilRoot

  • recoil 상태를 사용하는 컴포넌트는 부모 트리 어딘가에 나타나는 RecoilRoot가 필요하다.
    • 루트 컴포넌트가 RecoilRoot를 넣기에 가장 좋은 장소이다.
    • 아래와 같이 컴포넌트를 감싸준다.
    <RecoilRoot>
          <CharacterCounter />
    </RecoilRoot>

Atom

  • 상태(state)의 일부를 나타낸다.
  • Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다.
  • atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다.
    • atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트들이 재렌더링되는 결과가 발생할 것이다.
const textState = atom({
  key: 'textState', // unique ID (다른 atoms/selectors에 관하여)
  default: '', // default value (aka initial value)
});
  • 컴포넌트가 atom을 읽고 쓰게 하기 위해서는 useRecoilState()를 아래와 같이 사용하면 된다.
// CharacterCounter는 상위에서 <RecoilRoot>으로 감싸져있다.
function CharacterCounter() {
  return (
    <div>
      <TextInput />
      <CharacterCount />
    </div>
  );
}

function TextInput() {
  // useState와 모양이 같다. 				 여기는 atom
  const [text, setText] = useRecoilState(textState);

  const onChange = (event) => {
    setText(event.target.value);
  };

  return (
    <div>
      <input type="text" value={text} onChange={onChange} />
      <br />
      Echo: {text}
    </div>
  );
}

Atoms

  • Atoms는 애플리케이션 상태의 source of truth를 갖는다.

  • todo 리스트에서 source of truth는 todo 아이템을 나타내는 객체로 이루어진 배열이 될 것이다.

  • atom 리스트를 todoListState 라고 하고 이것을 atom()를 이용해서 생성한다.

const todoListState = atom({
	key : 'todoListState',
    default : [],
})
  • atom에 고유한 key를 주고 비어있는 배열 값을 default로 설장헸다.
  • 이 atom의 항목을 읽기 위해, useRecoilValue() 훅을 컴포넌트에서 사용할 수 있다.
function TodoList() {
	const todoList = useRecoilValue(todoListState);
  	
  	return (
    	<>
        	<TodoItemCreator />
        	{todoList.map((todoItem) => (
        		<TodoItem key={todoItem.id}
        )
        </>
    )
}
  • 새로운 todo 아이템을 생성하기 위해 todoListState 내용을 업데이트하는 setter 함수에 접근해야 한다.
  • todoItemCreator 컴포넌트의 setter 함수를 얻기 위해 useSetRecoilState() 훅을 사용할 수 있다.
function TodoItemCreator() {
  const [inputValue, setInputValue] = useState('');
  // 새로운 todoList를 set해주는 부분
  const setTodoList = useSetRecoilState(todoListState);

  const addItem = () => {
    //	이전 todoList 데이터와 새로운 데이터를 합한 데이터가 todoListState로 업데이트 된다.
    setTodoList((oldTodoList) => [
      ...oldTodoList,
      {
        id: getId(),
        text: inputValue,
        isComplete: false,
      },
    ]);
    setInputValue('');
  };

  const onChange = ({target: {value}}) => {
    setInputValue(value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={onChange} />
      <button onClick={addItem}>Add</button>
    </div>
  );
}

// 고유한 Id 생성을 위한 유틸리티
let id = 0;
function getId() {
  return id++;
}
  • 기존 todo 리스트를 기반으로 새 todo 리스트를 만들 수 있도록 setter 함수의 updater 형식을 사용한다.
  • TodoItem 컴포넌트는 todo 리스트의 값을 표시하는 동시에 텍스트를 변경하고 항목을 삭제할 수 있다.
  • todoListState를 읽고 항목 텍스트를 업데이트하고, 완료된 것으로 표시하고, 삭제하는 데 사용하는 setter 함수를 얻기 위해 useRecoilState()를 사용한다.
function TodoItem({item}) {
  const [todoList, setTodoList] = useRecoilState(todoListState);
  const index = todoList.findIndex((listItem) => listItem === item);

  const editItemText = ({target: {value}}) => {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      text: value,
    });
	// newList는 수정한 todo
    // 아래에서 업데이트 해준다.
    setTodoList(newList);
  };

  // todo가 완료되었는지 확ㅇ니하는 부분
  const toggleItemCompletion = () => {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      isComplete: !item.isComplete,
    });
	// 수정한 todo를 업데이트
    setTodoList(newList);
  };

  const deleteItem = () => {
    // todo 삭제
    const newList = removeItemAtIndex(todoList, index);
 	// 변경 사항 업데이트
    setTodoList(newList);
  };

  return (
    <div>
      <input type="text" value={item.text} onChange={editItemText} />
      <input
        type="checkbox"
        checked={item.isComplete}
        onChange={toggleItemCompletion}
      />
      <button onClick={deleteItem}>X</button>
    </div>
  );
}

function replaceItemAtIndex(arr, index, newValue) {
  return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
}

function removeItemAtIndex(arr, index) {
  return [...arr.slice(0, index), ...arr.slice(index + 1)];
}
profile
전 척척학사지만 말하는 감자에요

0개의 댓글