KassyLog

리코일 본문

react

리코일

Kassy_kim 2023. 3. 27. 22:11

https://recoiljs.org/ko/

 

Recoil

A state management library for React.

recoiljs.org

전역 상태를 관리하는 라이브러리이며 전역 store에서 상태들을 관리하며 컴포넌트가 각 상태를 구독하고 있는 형태로 사용한다.

내부적으로 ref를 통해 상태를 전달하여 리렌더링을 방지할 수 있고, atomic concept를 차용하여 리액트 상태에 가깝게 사용할 수 있다는 장점이 있다.

 

atoms(공유상태)에서 selectors(순수함수)를 거쳐 리액트 컴포넌트로 내려가는 데이터플로우그래프를 만들 수 있다.

 

atoms:  컴포넌트가 구독할 수 있는 상태의 단위

  • 고유한 키, 기본값 필요
  • atom을 읽고 쓰려면 useRecoilState라는 훅을 사용
    • React의 useState와 비슷하지만 상태가 컴포넌트 간에 공유될 수 있다는 차이가 있다.

selectors: atoms 상태값을 동기 또는 비동기 방식을 통해 변환

  • 상위의 selector 가 업데이트되면 하위의 selector 함수도 다시 실행된다.
  • 상태를 기반으로 하는 파생 데이터를 계산하는데 사용
  • 최소한의 상태 집합만 atoms에 저장하고 다른 모든 파생되는 데이터는 selector에 명시한 함수를 통해 효율적으로 계산함으로써 쓸모없는 상태의 보존을 방지한다.
  • useRecoilValue()를 사용해 읽을 수 있다. useRecoilValue()는 하나의 atom이나 selector를 인자로 받아 대응하는 값을 반환

 

*내가 작성한 예시 코드

import React, { useState } from "react";
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
  useSetRecoilState,
} from "recoil";

const todoListState = atom({
  key: "todoListState",
  default: [],
});
//filter _ selector활용
const todoListFilterState = atom({
  key: "todoListFilterState",
  default: "Show All",
});
const filteredTodoListState = selector({
  key: "filteredToDoListState", //todoListFilterState, todoListState 의존 따라서 이 두개가 변경되면 재실행
  get: ({ get }) => {
    const filter = get(todoListFilterState);
    const list = get(todoListState);

    switch (filter) {
      case "Show Complted":
        return list.filter((item) => item.isComplete);
      case "Show Uncompleted":
        return list.filter((item) => !item.isComplete);
      default:
        return list;
    }
  },
});
function App() {
  return (
    <RecoilRoot>
      <TodoList />
    </RecoilRoot>
  );
}
function TodoList() {
  const todoList = useRecoilValue(filteredTodoListState); //atom을 읽고 쓰기 위한 훅
  return (
    <div>
      <TodoListStats />
      <TodoListFilters />
      <TodoItemCreator />

      {todoList.map((item) => (
        <TodoItem key={item.id} item={item} />
      ))}
    </div>
  );
}

//view 컴포넌트
function TodoItemCreator() {
  const [inputValue, setInputValue] = useState("");
  const setTodoList = useSetRecoilState(todoListState); //setter함수

  const addItem = () => {
    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++;
}

function TodoItem({ item }) {
  const [todoList, setTodoList] = useRecoilState(todoListState); //setter힘수를 얻기위해
  const index = todoList.findIndex((listItem) => listItem === item);

  const editItemText = ({ target: { value } }) => {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      text: value,
    });
    setTodoList(newList);
  };
  const toggleItemCompletion = () => {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      isComplete: !item.isComplete,
    });

    setTodoList(newList);
  };

  const deleteItem = () => {
    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)];
}

function TodoListFilters() {
  const [filter, setFilter] = useRecoilState(todoListFilterState);
  const updateFilter = ({ target: { value } }) => {
    setFilter(value);
  };
  return (
    <>
      Filter :
      <select value={filter} onChange={updateFilter}>
        <option value="Show All">All</option>
        <option value="Show Completed">Completed</option>
        <option value="Show Uncompleted">Uncompleted</option>
      </select>
    </>
  );
}
const todoListStatsState = selector({
  key: "todoListStatsState",
  get: ({ get }) => {
    const todoList = get(todoListState);
    const totalNum = todoList.length;
    const totalCompletedNum = todoList.filter((item) => item.isComplete).length;
    const totalUncompletedNum = totalNum - totalCompletedNum;
    const percentCompleted = totalNum === 0 ? 0 : totalCompletedNum / totalNum;

    return {
      totalNum,
      totalCompletedNum,
      totalUncompletedNum,
      percentCompleted,
    };
  },
});
function TodoListStats() {
  const { totalNum, totalCompletedNum, totalUncompletedNum, percentCompleted } =
    useRecoilValue(todoListStatsState);
  const formattedPercentCompleted = Math.round(percentCompleted);
  return (
    <ul>
      <li>Total items : {totalNum}</li>
      <li>Items completed : {totalCompletedNum}</li>
      <li>Items not completed : {totalUncompletedNum}</li>
      <li>Percent completed : {formattedPercentCompleted}</li>
    </ul>
  );
}
export default App;

 

공부 한 내용을 바탕으로 작성한 글이고 앞으로 더 작성할 예정이다.

'react' 카테고리의 다른 글

react 기본 다시 정리해보기!  (1) 2024.02.28
리액트 쿼리  (0) 2023.03.27
emotion과 styled-components  (0) 2023.03.26
craco란?  (0) 2022.12.16
Refresh Token  (0) 2022.12.15