❄️

Recoil Selectors やってみた

2022/01/13に公開

前回の続き
https://zenn.dev/marumarumeruru/articles/41806ec801aa53

Recoilの公式チュートリアルの続きをしました

https://recoiljs.org/docs/basic-tutorial/selectors

source

最終的な構成
recoil-app $ tree src
src
├── App.css
├── App.js
├── App.test.js
├── RecoilState.js
├── TodoItem.js
├── TodoItemCreator.js
├── TodoList.js
├── TodoListFilters.js
├── TodoListStats.js
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.js

0 directories, 14 files
RecoilState.js
import { atom , selector } from 'recoil';

export const todoListState = atom({
  key: 'todoListState',
  default: [],
});

export const todoListFilterState = atom({
  key: 'todoListFilterState',
  default: 'Show All',
});

export const filteredTodoListState = selector({
  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;
    }
  },
});

export 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 * 100;

    return {
      totalNum,
      totalCompletedNum,
      totalUncompletedNum,
      percentCompleted,
    };
  },
});
App.js変更なし
import { TodoList } from './TodoList';
import { RecoilRoot } from 'recoil';

const App = () =>  {
  return (
    <RecoilRoot>
      <h1>Todo List</h1>
      <TodoList/>
    </RecoilRoot>
  );
}

export default App;
TodoList.js
import { useRecoilValue } from 'recoil';
import { filteredTodoListState } from "./RecoilState";
import { TodoItemCreator } from './TodoItemCreator';
import { TodoItem } from './TodoItem';
import { TodoListStats } from './TodoListStats';
import { TodoListFilters } from './TodoListFilters';

export const TodoList = () => {
    const todoList = useRecoilValue(filteredTodoListState);

    return (
        <>
        <TodoListStats />
        <TodoListFilters />
        <TodoItemCreator />

        {todoList.map((todoItem) => (
            <TodoItem key={todoItem.id} item={todoItem} />
        ))}
        </>
    );
}
TodoListStats
import { useRecoilValue } from "recoil";
import { todoListStatsState } from "./RecoilState";

export const 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>
  );
}
TodoListFilters.js
import { useRecoilState } from "recoil";
import { todoListFilterState} from './RecoilState';

export const 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>
    </>
  );
}

run

npm start

Discussion