iT邦幫忙

2022 iThome 鐵人賽

DAY 25
0
自我挑戰組

新手前端與真實世界的開發 feat.React 與他的夥伴系列 第 26

Day-26 專案演練 - 在 homePage 拿到 list redux

  • 分享至 

  • xImage
  •  

Day-26 專案演練 - 在 homePage 拿到 list redux

今天我們要完成 homePage 的一部分功能,跟 todo 有關的部分,其實鐵人賽也到尾聲了,我要誠實的跟大家說,在鐵人賽的期間確定無法完成這個專案,不過也沒關係,在努力一下。

在開始之前

我寫了 26 篇,卻一直忘記一件事,今天就來跟大家說一下,其實我習慣會把我要做的事情一項一項寫下來,例如這樣 :

// 彈跳視窗
// 加入 list
// 把一個 todo 放到釘選位置
// 思考要釘選哪一種 todo

偷偷推薦一個我自己很喜歡的 vscode 套件 :Comment Anchors,我很常使用它來標記我的段落,也包含我自己接下來想寫的 code。

從 redux 的 store 中拿到 state

決定要做的事之後,首先,把 slice 引入進來之後,使用 Selector 來取得 state,中間 :

...
import { useAppSelector, useAppDispatch } from "../redux/hooks";
import {
  selectModalIsOpen,
  openModal,
  closeModal,
} from "../redux/modalSlice/modalSlice";
import { selectList, addTodo } from "../redux/todoSlice/todoSlice";
...
// const [isOpen, setIsOpen] = useState(false);
const isOpen = useAppSelector(selectModalIsOpen);
const list = useAppSelector(selectList);

console.log("list", list, "isOpen", isOpen);
...

在 HomePage 也增加 Modal

新增一個 home 資料夾,HomePage 放進來,把報錯清除,之後就先無腦的把 TodoModal 複製一份變成 HomeModal,相同的 code 靜待下一次重購的時候處理,記得從 homePage 傳入 必要的 props 喔 :

HomeModal.tsx

import React from "react";
import Modal from "../../components/Modal";

type Props = {
  isOpen?;
  closeModal;
  onSubmit;
  register;
  handleSubmit;
  reset;
};

const HomeModal: React.FC<Props> = ({
  isOpen,
  closeModal,
  onSubmit,
  register,
  handleSubmit,
  reset,
}) => {
  return (
    <Modal isOpen={isOpen}>
      <div className="flex justify-end">
        <span
          className="
          font-medium
          cursor-pointer 
          inline-block 
          bg-gray-200 
          w-[24px] text-center rounded"
          onClick={closeModal}
        >
          X
        </span>
      </div>

      <div className="py-12 flex flex-col justify-center items-center">
        <h2 className="text-2xl font-bold">新增待辦</h2>
        <div className="mt-8">
          <form onSubmit={handleSubmit(onSubmit)}>
            <div className="grid grid-cols-1 gap-[8px] w-[340px]">
              <label className="block ">
                <span className="text-gray-700">標題</span>
                <input
                  type="text"
                  className="
                    mt-1
                    block
                    w-full
                    rounded-md
                    bg-gray-100
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
                  {...register("title", { required: true })}
                />
              </label>

              <label className="block">
                <span className="text-gray-700">時間</span>
                <input
                  type="date"
                  className="
                    mt-1
                    block
                    w-full
                    rounded-md
                    bg-gray-100
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
                  {...register("time")}
                  value="2018-07-22"
                />
              </label>
              <label className="block">
                <span className="text-gray-700">詳情描述</span>
                <textarea
                  className="
                    mt-1
                    block
                    w-full
                    rounded-md
                    bg-gray-100
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
                  rows={3}
                  {...register("info")}
                />
              </label>
              <label className="flex justify-center items-center">
                <input
                  type="submit"
                  className="
                    mt-1
                    block
                    w-full py-[4px]
                    cursor-pointer
                    rounded-md
                    bg-gray-300
                    border-transparent
                    focus:border-gray-500 focus:bg-white focus:ring-0
                  "
                />
              </label>
            </div>
          </form>
        </div>
      </div>
    </Modal>
  );
};

export default HomeModal;

加入 addTodo

跟 todoPage 一樣,加一下吧~

      ...
      <HomeModal
        isOpen={isOpen}
        closeModal={() => dispatch(closeModal())}
        register={register}
        onSubmit={onSubmit}
        handleSubmit={handleSubmit}
        reset={reset}
      />
      ...
       <AddButton onClick={() => dispatch(openModal())} />

選擇要釘選的 todo

思考許久,我打算用最近一次新增的 todo 當作釘選的目標,所以我們要來把最近一次新增 todo 給揪出來,目前新加入的 todo 都是從後面加,所以就抓最後一個項目,新增一個 state 讓 home 持有 :

...
 const [todo, setTodo] = useState(()=>list[list.length - 1]);
 ...

把 UI 加上去

加上這一步就算完成。

...
 <div className="flex flex-col justify-center ml-[8px] px-[8px]  w-[800px] pr-[8px]">
            <div className="w-full flex">
              <div className="w-2/12 flex items-center justify-center bg-gray-200 rounded-l-xl">
                已釘選
              </div>
              <div className="bg-slate-50 w-full pl-[8px]">
                <h3 className="">{todo.title}</h3>
                <div className="text-sm">{todo.info}</div>
              </div>
            </div>
          </div>
          ...

最後的 HomePage :

import React from "react";
import { useState } from "react";
import AddButton from "../../features/AddButton";
import HomeModal from "./HomeModal";
import { useAppSelector, useAppDispatch } from "../../redux/hooks";
import {
  selectModalIsOpen,
  openModal,
  closeModal,
} from "../../redux/modalSlice/modalSlice";
import { selectList, addTodo } from "../../redux/todoSlice/todoSlice";
import { useForm } from "react-hook-form";

const HomePage: React.FC = () => {
  const dispatch = useAppDispatch();
  const isOpen = useAppSelector(selectModalIsOpen);
  const list = useAppSelector(selectList);

  const [todo, setTodo] = useState(()=>list[list.length - 1]);

  const {
    register,
    handleSubmit,
    watch,
    reset,
    formState: { errors },
  } = useForm();
  console.log("list", list, "isOpen", isOpen);
  const onSubmit = (data) => {
    // setList([...list, data]);
    dispatch(addTodo(data));
    if (isOpen) dispatch(closeModal());
    reset();
    console.log("list", list);
  };
  return (
    <>
      <HomeModal
        isOpen={isOpen}
        closeModal={() => dispatch(closeModal())}
        register={register}
        onSubmit={onSubmit}
        handleSubmit={handleSubmit}
        reset={reset}
      />

      <section className="max-w-[1200px] mx-auto my-0 pt-[40px]">
        <div className="flex items-center justify-between">
          <AddButton onClick={() => dispatch(openModal())} />
          <div className="flex flex-col justify-center ml-[8px] px-[8px]  w-[800px] pr-[8px]">
            <div className="w-full flex">
              <div className="w-2/12 flex items-center justify-center bg-gray-200 rounded-l-xl">
                已釘選
              </div>
              <div className="bg-slate-50 w-full pl-[8px]">
                <h3 className="">{todo.title}</h3>
                <div className="text-sm">{todo.info}</div>
              </div>
            </div>
          </div>
        </div>
        <div className="pt-[20px]">
          <ul className="flex justify-between">
            <li className="p-[8px] mr-[16px] h-[600px] w-[450px] bg-gray-300">
              鐵櫃
            </li>
            <li className="p-[8px] mr-[16px] h-[600px] w-[450px] bg-gray-300">
              書櫃
            </li>
            <li className="p-[8px] mr-[16px] h-[600px] w-[450px] bg-gray-300">
              桌子
            </li>
            <li className="p-[8px] h-[600px] w-[450px] bg-gray-300">冰箱</li>
          </ul>
        </div>
      </section>
    </>
  );
};

export default HomePage;

repo

附上程式碼

結語

今天完成了 todo 釘選的部分,成就感很微妙,堅持到今天了呢,繼續再接再厲。


上一篇
Day-25 專案演練 - 創造全局待辦清單 redux
下一篇
Day-27 專案演練 - 寫在最後
系列文
新手前端與真實世界的開發 feat.React 與他的夥伴30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言