---
# System prepended metadata

title: Side Project learn 03. React Search Anime / 專案修改與紀錄
tags: [React Project]

---

### 代碼倉

[https://github.com/WilsonCode1224/Anime-search](https://github.com/WilsonCode1224/Anime-search)

### 待修正事項：

- 11/20 待修正事項:
- [x] isSearch 應該要用 dispatch
- [x] HomePage 用 Map
- [x] AnimaItem map
- [x] Iframe 意義
- [x] Upcoming render() + map問題   (目前合併起來解決。)
- [x] 額外 濃縮成一個 component Airing/Popular/UpComing



### 11/20 工作日誌：

1. 11:30 ~ 12:00 - 調整 網址 URL 結構。
2. 12:00 ~ 12:30 - HomePage 用 Map。 

```jsx
// 2. Header Button
const HeaderButtons = (setRendered, getAiringAnime, getUpcomingAnime) => [
  {
    label: "Popular",
    onClick: () => {
      setRendered("popular");
    },
    icon: <i className="fas fa-fire" />,
  },
  {
    label: "Airing",
    onClick: () => {
      setRendered("airing");
      getAiringAnime();
    },
  },
  {
    label: "Upcoming",
    onClick: () => {
      setRendered("upcoming");
      getUpcomingAnime();
    },
  },
];


// render
{buttons.map((button, index) => (
    <div key={index} className={button.className}>
        <button onClick={button.onClick}>
            {button.icon}
            {button.label}
        </button>
    </div>
))}
```

3. 12:30 ~ 13:00 - AnimaItem map
```jsx
  const detailsData = [
    { label: "Aired", data: aired?.string },
    { label: "Rating", data: rating },
    { label: "Rank", data: rank },
    { label: "Score", data: score },
    { label: "Scored By", data: scored_by },
    { label: "Popularity", data: popularity },
    { label: "Status", data: status },
    { label: "Source", data: source },
    { label: "Season", data: season },
    { label: "Duration", data: duration },
  ];

// render
<div className="anime-details">
    {detailsData.map((detail, index) => (
        <p key={index}>
            <span>{detail.label}</span>
            <span>{detail.data}</span>
        </p>
    ))}
</div>
```
4. 13:00 ~ 13:30 Iframe 是什麼東西？

- iframe，是一個 HTML 標籤。
  用來嵌入其他 HTML 文件或外部網頁。
  
- 在該專案也有使用到這個 Tag：
  使用來嵌入動畫的預告片（trailer）。
  這樣的做法使得動畫的預告片可以直接在你的網頁上播放，而不需要跳轉到其他頁面或網站。

```jsx
      <div className="trailer-con">
        {trailer?.embed_url ? (
          <iframe
            src={trailer?.embed_url}
            title="Inline Frame Example"
            width="800"
            height="450"
            allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
            allowFullScreen
          ></iframe>
        ) : (
          <h3>Trailer not available</h3>
        )}
      </div>
```

5. 13:40 ~ 14:15 isSearch 應該要用 dispatch

```jsx
// add actions
  // actions
  const LOADING = "LOADING";
  const SEARCH = "SEARCH";
  const GET_POPULAR_ANIME = "GET_POPULAR_ANIME";
  const GET_UPCOMING_ANIME = "GET_UPCOMING_ANIME";
  const GET_AIRING_ANIME = "GET_AIRING_ANIME";
  const GET_PICTURES = "GET_PICTURES";
  const IS_SEARCH = "IS_SEARCH"; // 1
  const IS_NOT_SEARCHING = "IS_NOT_SEARCHING"; // 2


// actions type

  // reducer
  const reducer = (state, action) => {
    switch (action.type) {
      case LOADING:
        return { ...state, loading: true };
      case GET_POPULAR_ANIME:
        return { ...state, popularAnime: action.payload, loading: false };
      case SEARCH:
        return { ...state, searchResults: action.payload, loading: false };
      case GET_UPCOMING_ANIME:
        return { ...state, upcomingAnime: action.payload, loading: false };
      case GET_AIRING_ANIME:
        return { ...state, airingAnime: action.payload, loading: false };
      case GET_PICTURES:
        return { ...state, pictures: action.payload, loading: false };
      case IS_SEARCH:
        return { ...state, isSearch: true };
      case IS_NOT_SEARCHING:
        return { ...state, isSearch: false };
      default:
        return state;
    }
  };

  // 不要直接針對屬性控制 , 透過 dispatch 操作
  const handleSubmit = (e) => {
    e.preventDefault();
    if (search) {
      searchAnime(search); // 異步函數
      dispatch({ type: IS_SEARCH });
    } else {
      dispatch({ type: IS_NOT_SEARCHING });
      alert("Please enter a search term");
    }
  };
```

6. 14:15 ~ 14:40 中場休息，部署一下網站。
   - 自嗨一下(?)
![截圖 2023-11-20 下午3.16.48](https://hackmd.io/_uploads/S1T1OYdNT.png)



7. 15:00 ~ 17:00 額外濃縮成一個 component **Airing/Popular/UpComing**：

- 1. 建立一個整合邏輯的元件 - AnimatedList.jsx。
- 2. 將 AnimatedList.jsx 導入到 Homepage.jsx。




---


### 11/21 工作日誌：

- 待處理的 bugs、優化網站等細項:
- [x] 用戶搜尋資料以後，無法透過按鈕去取得對應的動漫資訊。

### 11/22 工作日誌：

- [x] - 研究 API 資料，看能不能，找出動漫分類。

```jsx
// 目前 data.data 可以查到的 types:
  const getAnimeGenres = async () => {
    //  genres/anime
    const response = await fetch(
      `https://api.jikan.moe/v4/top/anime?filter=bypopularity`
    );
    const data = await response.json();
    console.log(data.data[0].genres[0].name);
  };

//  console.log(item.genres[0].name);  -> 11/22 14:05
```
- [x] - 動漫分類過濾，開發元件或新功能。 - (有素材)

```
進度描述：
1. 目前僅能將 popularAnime [] 資料傳進來，
以這邊的資料為準設置了動漫分類，套用到 MUI 素材，並且開立一個分頁。

後續會先以 popular , airing , upcoming 的資料來源來去做分類寫邏輯。 

// 日後 api 資料有時間再做一個 Total 的 Filter。 
```
![截圖 2023-11-22 下午3.25.58](https://hackmd.io/_uploads/r17MTXjNa.png)

#### bug 修改及詠唱反饋：

1. 目前可以透過點擊 radio，type === 取得關聯的動畫。

```
UI 控制，切換 Light、Dark 模式，進度描述：
詠唱出了一段 UI，但真他媽醜 xd
目前還沒辦法控制全局套用。
```

---

### 11/23 工作日誌：

- 螢幕右下方有一個齒輪：
  - 點擊以後可以出現：
- [x]   - 齒輪設定完成，目前點擊可以產生更多查詢的面板。
    - 「查詢更多熱門分類」- popular。
    - 「查詢更多上映分類」- aircoming。
    - 「查詢更多待上映分類」- upcoming。


---
### 11/24 工作日誌：
- [x] - 透過 context，取得三份 initial State 資料原型。
- [ ] - 將上述功能畫面整合成一個元件：
    - 該元件可將三份來源資料整合在該元件「SearchMore.jsx」
    - 且點擊路由，就可取得對應的分類 radio，及其對應的動畫。
- [ ] - UI 開發，以 styled 為主要樣式開發工具。  
- [x] - 控制 Setting 路由 -> 熱門動畫 -> 取得資料。

---
### 11/25 工作日誌：

- [x] - 點擊 SearchMore 選單，可以取消 AnimatedList。
   - 棘手問題：因為一開始需要先載入 popular，有什麼辦法可以取消？
- [X] - 點擊以後，已經可以正確來回切換 AnimatedList。

1. 
```jsx
// 修改內容：

// context add State -> 傳遞到:
const [showAnimatedList, setShowAnimatedList] = useState(true); // 控制 AnimatedList View
```
2. 確認哪些元件有相對應的關聯性：
   - **Homepage：**
```jsx
// Homepage, HeaderButtons 變數：
const HeaderButtons = (
  setRendered,
  getAiringAnime,
  getUpcomingAnime
  // getPopularAnime
) => {
    // 沒有這個 setState , 無法接收控制狀態。
  const { setShowAnimatedList } = useGlobalContext();

  return [
    {
      label: "Popular",
      onClick: () => {
        setRendered("popular");
        setShowAnimatedList(true); // 显示 AnimatedList
      },
      icon: <i className="fas fa-fire" />,
    },
    {
      label: "Airing",
      onClick: () => {
        setRendered("airing");
        getAiringAnime();
        setShowAnimatedList(true); // 显示 AnimatedList
      },
    },
    {
      label: "Upcoming",
      onClick: () => {
        setRendered("upcoming");
        getUpcomingAnime();
        setShowAnimatedList(true); // 显示 AnimatedList
      },
    },
  ];
};

// Homepage jsx render:

<AnimatedList rendered={rendered} showAnimatedList={showAnimatedList} />
```
3. AnimatedList 需要這個 props:

```jsx
function AnimatedList({ rendered, showAnimatedList }) {
  if (!showAnimatedList) {
    return null; // 不渲染 AnimatedList (表示不是 true 就 null 掉)
  }

  const { popularAnime, airingAnime, upcomingAnime, isSearch, searchResults } =
    useGlobalContext();

  const getAnimeData = (rendered) => {
    switch (rendered) {
      case "popular":
        return popularAnime;

      case "airing":
        return airingAnime;

      case "upcoming":
        return upcomingAnime;

      default:
        return popularAnime;
    }
  };
```

- [x] - 測試：可以透過路由點擊以後，顯示 MorePopular!
- [x] - 透過點擊 SearchMore - 先去做到切換取得更多分類的「標題」。
- [X] - 標題切換以後，標題去判斷使組件去判別該從 Context 取得什麼「資料」。
```jsx
功能 & 邏輯已經完成，這邊需要在研究一下 MorePopular 組件邏輯。
```

### 11/26 工作日誌：

- [x] 釐清：Homepage、MoreSearch、MorePopular 元件新增語法。

#### 1. 在 Homepage 右下角，設置了一個 Setting Icon:
   - 用戶點擊以後，會在左方顯示一下展開的 Box，這邊是如何運作?

```jsx
// 1. 首先要在 Homepage.jsx 設置一個元件，這表示 SearchMore

import SearchMore from "./SearchMore"; -> 導入這塊元件。

// 2. 插入 render 元件 -> 
    <SearchMore
        setShowMorePopular={setShowMorePopular}
        handleCategoryClick={handleCategoryClick}
    />
            
// 3. 在 gobalContext 設置好 控制用的 State:
const [openSetting, setOpenSetting] = useState(false);
// 4. 並且傳入到 Children 給上下文傳遞在為一個使用:

- 這邊範例是直接在 SearchMore 做傳遞了：

  const {
    openSetting, // 打開樣版的 State
    setOpenSetting, // 打開樣版的 setState
    // 以下略過 ...
  } = useGlobalContext();
```
```jsx
// 5. 為何點擊之後可以產生及關閉搜索更多分類? 原因如下：

    <Link>
        <FixedSettingsIcon
        style={{ width: "40px", height: "40px" }}
// 6. 由於 setOpenSetting 預設為 false 所以點擊之後反向自然會變成 true。
        onClick={() => setOpenSetting(!openSetting)}
        />
    </Link>
```
```jsx
// 7. 下面則是顯現，當 openSetting true 時則會渲染以下內容：     
     {openSetting && ( // -> 相對的點擊後 false 則是隱藏
        <SettingPanel>
          <SearchTitle>
            Search More Anime
            {/* <SearchIcon className="search-icon" /> */}
          </SearchTitle>
          <ShowText>
            {SearchText.map((item, index) => (
              <div key={index}>
                <Link to="#" onClick={() => handleLinkClick(item.action)}>
                  {item.showText}
                </Link>
              </div>
            ))}
          </ShowText>
        </SettingPanel>
      )}
// 8. 而項目列表則是由 map method 方法跑出來的。
```
#### 2. 搜尋更多的路由，是怎麼去根據邏輯產生分類搜尋的?
- SearchMore 點擊路由，以後會去關閉 AnimatedList。
  - 切換成 MorePopular - 並且根據路由名稱去顯示對應的資料分類。
```jsx
// 1. 我們在前面有使用 map 去渲染路由，並且這些路由都會呼叫一個回調函數在上面。

<ShowText>
    {SearchText.map((item, index) => (
        <div key={index}>
            <Link to="#" onClick={() => 
                handleLinkClick(item.action)}>   // 在這邊
                  {item.showText}
            </Link>
        </div>
    ))}
</ShowText>

// 2. 用戶點擊連結，呼叫回調函數以後，讓我們來看到回調函數的內容：

// 3. 此時，我們可以根據我們所處的 action.type 去執行對應的 case 內容。

  const handleLinkClick = (action) => {
    // 根據點擊的連結，調用相應的函數
    switch (action) {
      case "getPopularAnime":
        // 這邊暫時不設置任何東西，因為熱門動畫會在一開始就載入。
        handleCategoryClick("popular"); // 告之父元素，現在做了哪些事？
        break;
      case "getAiringAnime":
        getAiringAnime();
        handleCategoryClick("airing"); // 告之父元素，現在做了哪些事？
        break;
      case "getUpcomingAnime":
        getUpcomingAnime();
        handleCategoryClick("upcoming"); // 告之父元素，現在做了哪些事？
        break;
      default:
        console.log("default case!");
        break;
    }
    setOpenSetting(!openSetting); // 關閉樣版
      
      // ShowAnimatedList 是 context 傳進來的
    setShowAnimatedList(false); // 更新 AnimatedList 為 false
    setShowMorePopular(true); // 更新 MorePopular 為 true
      -> 上面這兩個 setState 則是關鍵了，並將這些更新資訊
      作為 props 傳遞到 Homepage 元件
  };


// 4. props 怎麼傳遞？
   - 從這邊：
function SearchMore({ setShowMorePopular, handleCategoryClick }) {}
```
```jsx
// 5. 回到 Home 父組件：

- 這邊可以看到，子組件 SearchMore 設置了兩個 props
// renoder:

<SearchMore
    // 控制點擊分類的分類
    handleCategoryClick={handleCategoryClick}
    // 顯示 MorePopular
    setShowMorePopular={setShowMorePopular}
/>
        
// 從子元件的 props 傳遞資訊，回到 Homepage 元件這邊的代碼：
   -> 這邊傳遞的 category 參數會帶入到 setSelectedCategory
  const handleCategoryClick = (category) => {
    setSelectedCategory(category);
  };        
        
// selectedCategory 的 State
  const [selectedCategory, setSelectedCategory] = useState(null); // 這邊是 SearchMore 的部分

-> 根據上面所傳遞進來的資訊，selectedCategory(切換category)

//
-> render , 此時根據傳遞到上面代碼的 category 繼續傳遞到子組件
   去做相對應的渲染結果：

 
{showMorePopular && <MorePopular selectedCategory={selectedCategory} />}   


-> 這段代碼表示： 
當 showMorePopular  true 時，則顯示 MorePopular 組件，
並且將 selectedCategory 狀態藉由 props 的形式傳遞到下個組件使用。
(MorePopular)
```
#### 3. MorePopular 怎麼去決定渲染的分類。
- 根據三個路由的條件，去決定資料來源且顯示更多的 radio 選項：
```jsx
// 1. 根據上述的流程，影響 MorePopular 組件條件就是 Props:

{showMorePopular && <MorePopular selectedCategory={selectedCategory} />}

// 2. 根據傳遞的 props - selectedCategory 決定代碼的走向

oops , 這裏目前遇到了一些不太能理解的 bug , 待修正。 (暫時不影響結果)
```


---

### 11/27 工作日誌：

- [X] 確認一下需要 RWD 開發的部分。
```
// Homepage - 顯示更多搜尋的 Icon fixed 定位。 --- O
// 點擊 Icon fixed 以後，左側欄位顯示。 --- O
// 搜尋結果的圖片位置 - center。 --- O
// 點擊動漫清單，裡面的 RWD 修飾。 --- X
```
- [ ] 設計 UI，'Dark' & 'Light' mode 控制設計。
- [x] RWD 各種修理! --- 目前大概只剩點擊後的 AnimeItem.jsx 
- [x] HomePage
- [x] SearchMore
- [x] AnimeItem

### 11/30 工作日誌：

- [ ] - UI 控制，切換 Light、Dark 模式。 ([有素材]



### 12/1 工作日誌：

- [ ] - i18n 繁中化 zh?


### 待處理的任務：
- 該專案目前預計執行的項目：
(https://mui.com/material-ui/react-switch/))
- [ ] 把 className 換成 styled(CSS)
- [ ] i18n (中文化)
- [ ] global getAnime 函數封裝
---
- 未來有機會在修改的東西：
- [ ] - 增設我的最愛，至收藏頁面功能。
- [ ] 網址 URL 結構 / 認識一下。(知識)
- [ ] 瀏覽器引擎 發展史。(知識)
- [ ] build / bundler。(知識)  
