Recoil - ChoDragon9/posts GitHub Wiki

์ฐธ๊ณ ์ž๋ฃŒ

Core Concepts

Atoms

  • ์ƒํƒœ ์œ ๋‹›
  • ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ณ , ๊ตฌ๋…ํ•  ์ˆ˜ ์žˆ์Œ.
  • key๋Š” ๊ณ ์œ ํ•œ ํ‚ค๋ฅผ ํ• ๋‹นํ•จ
    • ๋””๋ฒ„๊น…, ์˜์†์„ฑ ๋“ฑ ์ด์ ์ด ์žˆ์Œ
const fontSizeState = atom({
  key: 'fontSizeState',
  default: 14,
})

useRecoilState

atom์„ ์ปดํฌ๋„ŒํŠธ์— ์‚ฌ์šฉํ•  ๋•Œ๋Š” useRecoilState๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

function FontButton () {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
      Click to Enlarge
    </button>
  );
}
function Text () {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return <p style={{fontSize}}>This text will increase in size too.</p>
}

Selectors

  • ์ˆœ์ˆ˜ํ•จ์ˆ˜
  • atom ๋˜๋Š” selector์˜ ์—…๋ฐ์ดํŠธ ๋ ๋•Œ ์žฌ๊ณ„์‚ฐ
const fontSizeLabelState = selector({
  key: 'fontSizeLabelState',
  get: ({get}) => {
    const fontSize = get(fontSizeState);
    const unit = 'px';
    return `${fontSize}${unit}`;
  }
});

useRecoilValue

selector์„ ์ปดํฌ๋„ŒํŠธ์— ์‚ฌ์šฉํ•  ๋•Œ๋Š” useRecoilValue๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

function FontButton () {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);
  return (
    <>
      <div>Current font size: ${fontSizeLabel}</div>
      <button onClick={() => setFontSize(fontSize + 1)} style={{fontSize}}>
        Click to Enlarge
      </button>
    </>
  );
}

Basic Tutorial

Atoms

const todoListState = atom({
  key: 'todoListState',
  default: [],
});
function TodoList() {
  const todoList = useRecoilValue(todoListState);
  return (
    <>
      {/* <TodoListStats /> */}
      {/* <TodoListFilters /> */}
      <TodoItemCreator />
      {todoList.map((todoItem) => (
        <TodoItem key={todoItem.id} item={todoItem} />
      ))}
    </>
  )
}

TodoItemCreator

function TodoItemCreator() {
  const [inputValue, setInputValue] = useState('');
  const setTodoList = useSetRecoilState(todoListState);
  
  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>
  )
}

let id = 0;
function getId() {
  return id++;
}

TodoItem

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,
    });

    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"
        checkbox={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)];
}

Selectors

const todoListFilterState = atom({
  key: 'todoListFilterState',
  default: 'Show All',
});
const filteredTodoListState = atom({
  key: 'filteredTodoListState',
  get: ({get}) => {
    const filter = get(todoListFilterState);
    const list = get(todoListState);
    
    switch (filter) {
      case 'Show Completed':
        return list.filter((item) => item.isComplete);
      case 'Show Uncompleted':
        return list.filter((item) => !item.isComplete);
      default:
        return list;
    }
  }
});

TodoList

function TodoList () {
  const todoList = useRecoilValue(filteredTodoListState);
  
  return (
    <>
      <TodoListStats />
      <TodoListFilters />
      <TodoListCreator />
      
      {todoList.map((todoItem) => (
        <TodoItem item={todoItem} key={todoItem.id} />
      ))}
    </>
  );
}

TodoListFilters

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>
    </>
  )
}

todoListStatsState

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
    }
  }
});

TodoListStats

function TodoListStats() {
  const {
    totalNum,
    totalCompletedNum,
    totalUnCompletedNum,
    percentCompleted
  } = useRecoilValue(todoListStatsState);

  const formattedPercentCompleted = Math.round(percentCompleted * 100);

  return (
    <ul>
      <li>Total items: {totalNum}</li>
      <li>Items completed: {totalCompletedNum}</li>
      <li>Items not completed: {totalUncompletedNum}</li>
      <li>Percent completed: {formattedPercentCompleted}</li>
    </ul>
  );
}
โš ๏ธ **GitHub.com Fallback** โš ๏ธ