yoshikipom Tech Blog

スーパーでの「来週なにつくる?」に答えを出すツールをReactで作った

なぜ作ったか

週1回の買い物時にできるだけ必要なものは買い揃えたい。 そのために何を作るか決める必要があるが、大体スーパーについてから考えてしまうのである程度自動化したい。

メイン機能

準備したリストの中からランダムな重複なしリストを取得するアプリケーション。 ここにデプロイしてあります。 https://yoshikipom.github.io/dinnerslot-web/

input
result

工夫点

  • 1アイテムだけチェンジする機能 -> 最近作った、材料がないなどでチェンジするための機能。全部チェンジするとなかなか理想の結果が得られないことがある。
  • 結果をLINEに出力するボタン(SHARE BY LINE ) -> 結果の永続化、シェア
  • 入力したリストはブラウザに保存 -> 入力時間削減

LINE share

利用技術

ソースコードと主要部分の実装

リポジトリ: https://github.com/yoshikipom/dinnerslot-web

ページは index.tsx のみ。 ページ内でListInput.tsx(リスト入力) と Slot.tsx(結果生成) を呼び出し、タブで出し分ける。

$ tree src 
src
├── components
│   ├── Footer.tsx
│   ├── LineShareButton.tsx
│   ├── ListInput.tsx
│   └── Slot.tsx
├── model
│   └── Food.ts
└── pages
    ├── _app.tsx
    └── index.tsx

結果生成 - 入力されたリストをコピー - ランダムに並び替え - 欲しい数だけ前から切り取る

    const slot = () => {
        const copiedFoodList = foodList.concat();
        for (let i = copiedFoodList.length - 1; i > 0; i--) {
            const r = Math.floor(Math.random() * (i + 1));
            const tmp = copiedFoodList[i];
            copiedFoodList[i] = copiedFoodList[r];
            copiedFoodList[r] = tmp;
        }
        updateResult(copiedFoodList.slice(0, Number(count)));
    }

一点チェンジ - 入力されたリストをコピー - 選ばれているアイテムを除去 - 候補がなければ終了 - ランダムで一点選択 - 上書き

    const slotOneItem = (index: number) => {
        const copiedFoodList: Food[] = foodList.concat();
        const remainingList: Food[] = copiedFoodList.filter((food) => !results.includes(food))
    
        if (remainingList.length == 0) {
            return;
        }

        const r = Math.floor(Math.random() * (remainingList.length));
        results[index] = remainingList[r];
        updateResult(results.slice(0, Number(count)));
    }

LINE Shareボタン - 「LINE URLスキーム」を利用 - ref https://developers.line.biz/ja/docs/line-login/using-line-url-scheme/ - 値はpropsで渡す

const LineShareButton = (props: any) => {
    return (
        <Button sx={{ mx: 2, my: 2 }} variant="outlined" href={`https://line.me/R/share?text=${encodeURI(props.message)}`}>
            Share by LINE
        </Button>
    );
};

入力リスト保存 - localStorage利用 - 取得は初回呼び出しのみ - 書き込みは入力(foodListInputRaw)が変更が変更するたび

    useEffect(() => {
        const foodListInputRaw = window.localStorage.getItem(STORAGE_KEY);
        if (foodListInputRaw !== null) {
            setFoodListInputRaw(foodListInputRaw);
        }
    }, []);

    useEffect(() => {
        const strList = foodListInputRaw.split('\n').filter(val => val);
        let index = 1;
        const newFoodList: Food[] = strList.map(name => ({ id: index++, name }));
        setFoodList(newFoodList);
        window.localStorage.setItem(STORAGE_KEY, foodListInputRaw);
    }, [foodListInputRaw, setFoodList]);

感想

localStorage、LINEシェアは初めて試したが簡単に使えてよかった。 技術の復習としてバックエンドも作りたかったが、要件的に不要になってしまい残念。 自分のほしいアプリケーションを作ると勉強したい技術が使えるとは