Closed11

動的にatomを増やしたい

hajimismhajimism

ざっくりいうとTODOリストの各アイテムをatomで管理したいみたいなイメージ。なのでそれでプロトタイプをやるか。

hajimismhajimism

なんで各アイテムをatomで管理したいかと言うと、バリデーションとかやるときにそっちのほうが便利なんじゃない?っていう発想

hajimismhajimism

これを使う。TODOリストの例も載ってるけど編集機能がほしい。
https://jotai.org/docs/utilities/family

hajimismhajimism

とりあえずこういう感じ

export type Todo = {
  id: string;
  title: string;
  checked: boolean;
};


export const todoFamily = atomFamily(
  (param: Todo) => atom(param),
  (a, b) => a.id === b.id
);
hajimismhajimism

リストで表示するために別途idの配列を管理するatomが必要だな?

hajimismhajimism

こういうことかと思ったんだけど違うっぽい。
ふつうにできた。↑は作ったidをtodoIdsAtomに入れるの忘れてただけ

import { atom } from "jotai";
import { atomFamily } from "jotai/utils";

import { Todo } from "./type";

export const todoFamily = atomFamily(
  (param: Todo) => atom(param),
  (a, b) => a.id === b.id
);

export const todoIdsAtom = atom<string[]>([]);
export const todosAtom = atom<Todo[]>((get) => {
  const todoIds = get(todoIdsAtom);
  const todos = todoIds.map((id) =>
    get(
      todoFamily({
        id,
        // idで検査するため、下記は型合わせのための適当な値
        title: "",
        checked: false,
      })
    )
  );

  return todos;
});

hajimismhajimism

todosAtomを作らなくても、todoIdsでmapして各item内で値を取って来ればいいか。

      {ids.length !== 0 && (
        <ul>
          {ids.map((id) => (
            <TodoItem key={id} id={id} />
          ))}
        </ul>
      )}
const TodoItem: FC<{ id: string }> = ({ id }) => {
  const [todo, setTodo] = useAtom(
    todoFamily({ id, title: "", checked: false })
  );
  return <li key={todo.id}>{todo.title}</li>;
};
hajimismhajimism

bottom upという考え方を念頭に置くと2つ目のほうがそれっぽい気がする。

hajimismhajimism

うん、こういう要領で各アイテムを編集できることを確認した

const TodoItem: FC<{ id: string }> = ({ id }) => {
  const [todo, setTodo] = useAtom(
    todoFamily({
      id,
      // idで検査するため、下記は型合わせのための適当な値
      title: "",
      checked: false,
    })
  );
  const [editMode, setMode] = useState(false);

  const toggle = () => setTodo((prev) => ({ ...prev, checked: !prev.checked }));
  const edit = (text: string) => setTodo((prev) => ({ ...prev, title: text }));
  const onChange: ChangeEventHandler<HTMLInputElement> = (e) =>
    edit(e.target.value);

  return (
    <li key={todo.id} className="flex items-center gap-10">
      <Button onClick={() => setMode((mode) => !mode)}>
        {editMode ? "終了" : "編集"}
      </Button>
      <div className="flex items-center gap-2">
        <input
          id={todo.id}
          type="checkbox"
          checked={todo.checked}
          onChange={toggle}
        />
        {editMode ? (
          <Input value={todo.title} onChange={onChange} className="w-80" />
        ) : (
          <Label htmlFor={todo.id} className="px-3">
            {todo.title}
          </Label>
        )}
      </div>
    </li>
  );
};
hajimismhajimism

配列でTodoを持って各アイテムを編集しようと思うと、filterとかが必要なのでこっちのほうが便利だと思った。
しかし二重stateっぽいのは否めない。

このスクラップは2023/08/14にクローズされました