---
# System prepended metadata

title: AppWorks EC Project Review

---

# AppWorks EC Project Review
[Sample Code](https://github.com/AppWorks-School-Materials/Stylish-Front-End) 以下題目請搭配著看

## CRA
### 1. Describe the project file structure. 
原則：
1. Grouping by features or routes
2. Grouping by file type: 範例-[Atomic Design](https://bradfrost.com/blog/post/atomic-web-design/)
3. Avoid too much nesting

依[專案規模](https://blog.webdevsimplified.com/2022-07/react-folder-structure/),小至大(src folder)
![](https://i.imgur.com/MAvoXCE.png)![](https://i.imgur.com/SoHiEZX.png)![](https://i.imgur.com/zYLHo4V.png)
STYLiSH sample code採用中小規模
優點:檔案依Pages分類較容易理解,也不會有太多共用檔
缺點:專案規模變大時共用檔案會變多,pages資料夾實用性會降低
* **root資料夾**:幾乎沒檔案,除了index.js和app.js
* **pages資料夾**:裡面放一個root.js跟只有在此頁會用到的component或logic
* **component資料夾**:共用的component,理論上檔案不會多,因為大部分複雜的component會被放在Pages資料夾
* **context資料夾**:共用的React context files,若有使用Redux,可以再做調整
* **utils資料夾**:放通用function,建議是pure function,例如api相關(sample code把針對不同情境的fetch method存在api物件裡,可以在其他檔案依情境取用,且方便管理,類似library或services資料夾?)

其他常見的資料夾分類:
* **hooks資料夾**:共用的客制Hooks
* **assets資料夾**:filse isn't code related, images, css files, font files, etc.
* **data資料夾**:放data assets,例如JSON檔或global constant variables
![](https://i.imgur.com/7hUgfy7.png)


### 2. Introduce package.json/package-lock.json
* **package.json**: a versioning file that primarily contains the **list of dependencies** (libraries) your node.js project needs to run.
![](https://i.imgur.com/m2cLaK7.png)
`npm i <module>`時模組會被加入dependencies
script可自訂npm快捷指令
* **package-lock.json**(npm5之後) is a lockfile that contains information about the dependencies/packages with their **exact version numbers** that were installed for a node.js project.
![](https://i.imgur.com/4ZC4YXj.png)
The lockfile is **generated** and **re-generated** when node_modules or package.json is changed
![](https://i.imgur.com/S7kAc8P.png)
clone完專案執行`npm i`時,npm會去檢查**package-lock.json**,以確保clone下來的node_modules tree會一模一樣
* `npm i`:若發現模組版本跟package-lock不同,會re-write
`npm ci`:若發現模組版本跟package-lock不同,會報錯,主要用在test platforms, continuous integration, and deployment
* [pnpm](https://pnpm.io/zh-TW/):針對node.module節省磁碟空間的工具
* 要定期檢查套件有沒有更新,但更新會有成本和相容性問題,通常會開一條feature branch,並搭配test來檢查
## JavaScript
### 1. Describe primitive data type & reference data type. What do we need to be aware of when using them?
- 基本型別 (Primitive Types)
    - 不可改變、沒有 method
    - Number, String, Boolean, Undefined, Null,  Symbol, BigInt
    - 值 (value) → 基本值 (primitive value)
- 物件型別 (Reference Types)
    - 可改變、有method
    - Primitive 之外的都是(ex: Object, Array, Function)

操作`setState(reference type)`時建議使用`...`來複製一個新版本,直接改原本的資料react會當成state沒有改變(判斷依據reference位址)
```
//setState with Object
// bad practice: modifying the original state directly
this.setState({
  user: {
    name: 'John',
    age: 30
  }
});

// good practice: creating a new state copy and updating it
this.setState(prevState => ({
  user: {
    ...prevState.user,
    city: 'New York'
  }
}));
```
Array Method也需注意:
常見錯誤：`push`, `pop`, `reverse`, `sort` 會改變original array並回傳undefined,結果變成`setState(undefined)`
推薦使用`slice`, `filter`, `map` 會回傳new Array
```
//setState with Array
// bad practice: modifying the original state directly
this.setState({
  todos: ['task1', 'task2', 'task3']
});

// good practice: creating a new state copy and updating it
this.setState(prevState => ({
  todos: [...prevState.todos, 'task4']
}));
```
### 2. What's the difference of shallow copy & deep copy? How to deep copy an array/object? 
#### shallow copy (非獨立,指向同一個reference)
```
const originalArray = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
  { id: 3, name: 'Charlie' },
];

// 淺拷貝
const shallowCopyArray = originalArray;

// 修改新数组的第一个对象的属性,原始数组的第一个对象的名称也会被修改，因它们共享同一个reference
shallowCopyArray[0].name = 'Alex';

console.log(originalArray[0].name); // 输出 'Alex'
```
#### deep copy (獨立,指向不同reference)
1. JSON.parse() and JSON.stringify() methods: 
```
const originalObj = { name: 'John', age: 30 };
const newObj = JSON.parse(JSON.stringify(originalObj));
```
* regExp會被轉為空物件`{}`
* `undefined`會被轉為`null`
* `typeOf null`會被轉為`{}`
* `typeOf NaN`會被轉為`number`
2. spread operator (...):
```
//Array
const originalArr = [1, 2, [3, 4]];
const newArr = [...originalArr];

//Object
const originalObj = { name: 'John', address: { city: 'New York', country: 'USA' } };
const newObj = { ...originalObj };
```
3. Object.assign() method:
```
//Array
const originalArr = [1, 2, [3, 4]];
const newArr = Object.assign([], originalArr);

//Objecu
const originalObj = { name: 'John', age: 30 };
const newObj = Object.assign({}, originalObj);
```
4. [Lodash](https://lodash.com/) | [Immer](https://immerjs.github.io/immer/):深拷貝套件
### 3. What are truthy/falsy values? Compare the usage of `== === ! ? || &&`
#### Falsy Value
* `false`
* `0`
* `''` 或 `""`（空字符串）
* `null`
* `undefined`
* `NaN`
#### Truthy Value
除Falsy Value之外的所有值都被视为 truthy 值。例如，`true`、非空字符串、數字、Object、Array等都是 truthy value
#### `==` `===`差別
`==`:類型轉換後比較值
`===`:嚴格比較,包括類型(開發時建議使用這個)
#### `！` 反操作：將 truthy 值轉換為 `false`，將 falsy 值轉換為 `true`
#### [? ｜ ?. ｜ ??](https://ithelp.ithome.com.tw/articles/10251001)
* `?` 三元運算符,表達式若為true,執行前面;若為false,執行後面
```
border: ${(props) => (props.selected ? "5px solid green" : "none")};
```
* `?.` 可選鏈運算符（optional chaining operator）用來簡化對Object/Array層次很深的屬性值或元素的操作
```
const obj = {
  foo: {
    bar: {
      baz: 42,
    },
  },
};

console.log(obj?.foo?.bar?.baz); // 輸出：42

console.log(obj?.foo?.qux?.baz); // 輸出：undefined
console.log(obj.foo.qux.baz); // Uncaught TypeError: Cannot read property ...
```
* `??`空值合並運算符（nullish coalescing operator）用于为变量设置默认值。如果一个变量的值为 null 或 undefined，那么它的值将被替换为运算符右侧的默认值。
```
let name;
console.log(name ?? 'Unknown'); // return:Unknown

let age = 0;    //值为0或空字符串 ''，不会被认为是空值，不会被替换为默认值。
console.log(age ?? 18); // return:0
```
#### short-circuit evaluation
搭配`&&` `||`使用,特点是只要能够确定整个表达式的值，就不再计算表达式的剩余部分
```
const name = user && user.name;    //如果 user 存在，就会返回 user.name 的值；否则，返回 undefined

const value = a || b();    //如果a為falsy,會執行b函數,如果b函數有side effect,可能會倒置意外狀況
```
1. 訪問Object的屬性
與`??`差別:左側為falsy值時return false / 左側為null或undefined時return右側
```
const obj = { prop: 'value' };
const result = obj.prop && obj.prop.length;    //return:5

======

const obj = {};
const result = obj.prop && obj.prop.length;    //return:false
```
2. function参数的默认值
與`??`差別:左側為falsy值時return右側 / 左側為null或undefined時return右側
```
function greet(name) {
  name = name || 'Guest';
  console.log(`Hello, ${name}!`);    //调用 greet 函数时没有传入 name 参数，则 name 会被设置为默认值 'Guest'，从而避免了错误
}
```
#### 常犯錯誤
```
口味是香草或是巧克力
正確: if (flavor === 'vanilla' || flavor === 'chocolate') 
錯誤: (flavor === 'vanilla' || 'chocolate')
```
```
錯誤
var category = (eatsPlants && eatsAnimals) ? 'omnivore' :
               eatsPlants ? 'herbivore' :
               eataAnimals ? 'carnivore' : 'undefined';

正確
var category = eatsPlants ? (eatsAnimals ? 'omnivore' : 'herbivore') : (eatsAnimals ? 'carnivore' : 'undefined');
```
#### TypeOf深度研究
[超屌等於圖表](https://dorey.github.io/JavaScript-Equality-Table/)
```
let falsyArray = [false, 0, '', null, undefined, NaN];
falsyArray.map(i => Boolean(i)); // [ false, false, false, false, false, false ]
falsyArray.map(i => typeof(i));  // [ "boolean", "number", "string", "object", "undefined", "number" ]
falsyArray.map(i => Number(i));  // [ 0, 0, 0, 0, NaN, NaN ]
falsyArray.map(i => String(i));  // [ "false", "0", "", "null", "undefined", "NaN" ]
```
- 整個JS就只有一個null 所以都指向同一個object
- NaN永遠不會等於任何東西！(唯一一個不等於自己的number)
  -> 判斷某變數(maybeNaN)是不是NaN要用
```  
[c]if((maybeNaN !== maybeNaN) === true)[end] 
```
or
```
[c]Number.isNaN(maybeNaN)[end] 
```
- undefinded 跟 null 永遠只會等於（==）自己或對方！
### 4. How to create a [promise](https://www.casper.tw/development/2020/02/16/all-new-promise/) to handle asynchronous operations? And how to chain multiple promises?
![](https://i.imgur.com/D0OjWhP.png)
舉例:
```
function fillE-portfolio() {
  const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      const isFilled = Math.random() >= 0.5;    //隨機決定有沒有忘記填
      if (isFilled) {
        resolve('填完了！');
      } else {
        reject('忘記了！');
      }
    }, 7000);    //7000代表一週
  });
  return promise;
}

const checkE-portfolio = () => {
  fillE-portfolio()
    .then((result) => {
      console.log('資料已加入後台');
    })
    .catch((error) => {
      console.error();
      console.log('請趕快補填！');
    })
    .finally(() => {
      console.log('去AppWorks School');
    });
};

checkE-portfolio();
```
#### Promise的方法
![](https://i.imgur.com/jg41zeU.png)

Sample Code-臉書login
```
const fb = {
...
  login() {
    return new Promise((resolve) => {
      window.FB.login(
        (response) => {
          resolve(response);
        },
        { scope: 'public_profile,email' }
      );
    });
  },
...
}

const login = async () => {
    const response = await fb.login();
    if (response.status === 'connected') {
      const tokenFromServer = handleLoginResponse(response);
      return tokenFromServer;
    } else {
      window.localStorage.removeItem('jwtToken');
      setLoading(false);
      return null;
    }
  }
```
#### fetch回來的資料格式[Cors](https://shubo.io/what-is-cors/)
### 5. What is async/await (ES7)? How to transform promises to async/await?
2. `try-catch`语句适用于需要直接在async function內部处理异常的情况,可以通过throw关键字抛出异常，然后由catch语句捕获异常进行处理。
`then().catch()`适用于需要链式调用多个异步操作并对每个操作的异常进行分离处理的情况。
3. 非同步代碼的同步化：使用async/await可以讓非同步代碼看起來像同步代碼一樣
```
const checkE-portfolio = async () => {
  try {
    const result = await fillE-portfolio();
    console.log('資料已加入後台');
  } catch (error) {
    console.error(error);
    console.log('請趕快補填！');
  } finally {
    console.log('去AppWorks School');
  }
};

checkEportfolio();
```
#### catch不到錯誤的狀況
```
async function test() {
  setTimeout(() => {
    throw new Error('Async error');
  }, 0);
}

try {
  await test();
} catch (error) {    //抓不到
  console.log('Caught error:', error);
}

test().then(() => {
  console.log('Success');
}).catch((error) => {    //抓得到
  console.log('Caught error:', error);
});
```
### 6. What is good naming?
1. 變數和常數名稱應該具有描述性，以便於理解其用途和值。使用有意義的單詞或單詞組合，不要使用縮寫或不必要的簡寫。
變數：變數名应该使用小写字母的驼峰命名法，描述變數的功能和类型。例如：userName、age、totalPrice等。
常數：用下划线分隔单词，表示其值不可改变。例如：MAX_NUMBER、DEFAULT_COLOR等。
2. 函數名稱應該描述其功能或操作，使用動詞加名詞的方式，例如：calculateTotal()、getUserInfo()。
3. 如果functionc或變數結果是Boolean,用is或has開頭
4. 組件名稱應該具有描述性，使用首字母大寫的駝峰式命名法（PascalCase），例如：LoginForm、ShoppingCart、ProductList。
5. 檔案名稱應該使用有意義的名稱，以便於識別和管理檔案。使用駝峰式命名法，例如：userProfile.js、productDetails.css。
6. 抽象化命名法：css避免使用left/right這種之後會變動的命名
7. function前面_字號：強調這個函式是私有的
```
 const [cartItems, _setCartItems] = useState(() => {
    return JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || [];
  });
 const setCartItems = (newItems) => {
    _setCartItems(newItems);
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newItems));
  };
```
## React: 介紹用法,sample code怎麼使用
#### [Strict Mode](https://beta.reactjs.org/reference/react/StrictMode): 執行兩次,放大效果較容易發現錯誤
### 1. JSX
1. Return a single root element (can write `<>` and `</>`) : 因為JSX是一個Object,沒辦法一次回傳兩個Object
2. Close all the tags
3. camelCase
4. JavaScript in JSX with `{Curly Braces}`
Using `{{double curlies}}`: CSS and other objects in JSX
5. map產生的JSX key={獨特id} 跟reference有關
[UUID](https://www.uuidgenerator.net/)套件
6. 須透過webpack/[babel](https://babeljs.io/)轉譯
7. 命名注意:className / htmlFor / Component
8. CRA環境副檔名.js .jsx都可(其他不一定),故Component要大寫
### 2. useState 
什麼不是state?
* Does it **remain unchanged** over time? If so, it isn’t state.
* Is it passed in from a parent **via props**? If so, it isn’t state.
* Can you **compute it based on existing state or props** in your component? If so, it isn’t state.
* [Rules](https://reactjs.org/docs/hooks-rules.html): react render順序問題
-Only Call Hooks at the Top Level
-Don’t call Hooks from regular JavaScript functions(loop,if...else等等都不行)
#### 官方reset state的方法
* [Avoiding recreating the initial state ](https://beta.reactjs.org/reference/react/useState#avoiding-recreating-the-initial-state)
* [Resetting state with a key](https://beta.reactjs.org/reference/react/useState#resetting-state-with-a-key)
### 3. useEffect
[Effects & cleanups](https://ithelp.ithome.com.tw/articles/10307558?sc=iThelpR) | [Synchronizing with Effects
](https://beta.reactjs.org/learn/synchronizing-with-effects#how-to-handle-the-effect-firing-twice-in-development)
* 是隨著每次 render 後而自動觸發的
* dependencies 是一種「忽略某些不必要的同步」的效能最佳化，而不是用來控制 effect 發生在特定的 component 生命週期，或特定的商業邏輯時機
* 應設計成即使多次重複執行也有保持行為正確的彈性
### 4. useRef
拿來存值,值跟畫面render沒關聯
用來選取DOM元素
* You can **store information between re-renders** (unlike regular variables, which reset on every render).
* Changing it **does not trigger a re-render** (unlike state variables, which trigger a re-render).
* The information is **local to each copy** of your component (unlike the variables outside, which are shared).

sample-code-用tappay的功能[填寫/驗證？]信用卡資料
```
  const cardNumberRef = useRef();
  const cardExpirationDateRef = useRef();
  const cardCCVRef = useRef();
  
    useEffect(() => {
    const setupTappay = async () => {
      await tappay.setupSDK();
      tappay.setupCard(
        cardNumberRef.current,
        cardExpirationDateRef.current,
        cardCCVRef.current
      );
    }
    setupTappay();
  }, []);
  
  
//return JSX
 ...
      <FormFieldSet>
      <FormLegend>付款資料</FormLegend>
      <FormGroup>
        <FormLabel>信用卡號碼</FormLabel>
        <FormControl as="div" ref={cardNumberRef} />
      </FormGroup>
      <FormGroup>
        <FormLabel>有效期限</FormLabel>
        <FormControl as="div" ref={cardExpirationDateRef} />
      </FormGroup>
      <FormGroup>
        <FormLabel>安全碼</FormLabel>
        <FormControl as="div" ref={cardCCVRef} />
      </FormGroup>
    </FormFieldSet>
```
sample-code-若form有漏填,頁面會滑到form的位置
```
const formRef = useRef();
//checkout扭按下後
async function checkout() {
  ...
  if (Object.values(recipient).some((value) => !value)) {
    window.alert('請填寫完整訂購資料');
    setInvalidFields(Object.keys(recipient).filter(key => !recipient[key]))
    formRef.current.scrollIntoView({    //滑～～
      behavior: "smooth",    //順～～
      block: "center",
    });
    return;
  }
  ...
}      
//return JSX
  <form ref={formRef}>
  ...
  </form>
```
### 5. useContext(optional)
Sample code-Context provider
```
import { createContext, useState } from 'react';

export const CartContext = createContext({
  cartItems: [],
  setCartItems: () => {}
});

const STORAGE_KEY = 'cartItems';

export const CartContextProvider = ({ children }) => {
  const [cartItems, _setCartItems] = useState(() => {
    return JSON.parse(window.localStorage.getItem(STORAGE_KEY)) || [];
  });
  const setCartItems = (newItems) => {
    _setCartItems(newItems);
    window.localStorage.setItem(STORAGE_KEY, JSON.stringify(newItems));
  };
  const cartCount = cartItems.reduce((total, item) => total + item.qty, 0);
  return (
    <CartContext.Provider
      value={{
        cartItems,
        setCartItems,
        cartCount
      }}
    >
      {children}
    </CartContext.Provider>
  );
};
```
Sample code-App.js
```
import { CartContextProvider } from './context/cartContext';

function App() {
  return (
    <>
      <Reset />
      <GlobalStyle />
      <AuthContextProvider>
        <CartContextProvider>    //provide設為共同父層
          <Header />
          <Outlet />
          <Footer />
        </CartContextProvider>
      </AuthContextProvider>
    </>
  );
}
```
Sample code-商品頁加入購物車-更新購物車資料
```
import { CartContext } from '../../context/cartContext';

const { cartItems, setCartItems } = useContext(CartContext);

...

  let newCartItems;
  const index = cartItems.findIndex(
    (item) =>
      item.color.code === selectedColorCode &&
      item.size === selectedSize
  );
  if (index > -1) {
    newCartItems = cartItems.map((item) => ({
      ...item,
      qty: item.qty + quantity
    }));
  } else {
    newCartItems = [
      ...cartItems,
      {
        color: product.colors.find(
          (color) => color.code === selectedColorCode
        ),
        id: product.id,
        image: product.main_image,
        name: product.title,
        price: product.price,
        qty: quantity,
        size: selectedSize,
        stock: getStock(selectedColorCode, selectedSize)
      }
    ];
  }
  setCartItems(newCartItems);
```
Sample code-購物車頁面-更新購物車資料
```
import { CartContext } from '../../context/cartContext';

...

const { cartItems, setCartItems } = useContext(CartContext);

  function changeItemQuantity(itemIndex, itemQuantity) {
    const newCartItems = cartItems.map((item, index) =>
      index === itemIndex
        ? {
            ...item,
            qty: itemQuantity
          }
        : item
    );
    setCartItems(newCartItems);
    window.alert('已修改數量');
  }
  
  function deleteItem(itemIndex) {
    const newCartItems = cartItems.filter((_, index) => index !== itemIndex);
    setCartItems(newCartItems);
    window.alert('已刪除商品');
  }
```
Sample code-結帳同理`setCartItems([]);`
### 6. Compare server-side & client-side routing. How to use react-router-dom to handle client side routing?
[CSR 和 SSR 的差別](https://shubo.io/rendering-patterns/)
React Router幫助我們達成SPA（Single Page Application）
* `Route` 主要是用來定義路徑跟 Components 之間的關係，"綁定"的概念,只能包在 `<Routes>` 底下使用
`path`：定義路徑
`element`：定義對應的元件
```
<Route path="/todolist" element={ <ToDoList/> }>
```
* `Routes`裡面有不同的`<Route>`
```
<Routes>
  <Route path="/" element={ <App /> } />
  <Route path="/todolist" element={ <ToDoList /> } />
</Routes>
```
* 巢狀路由: **共用 Layout 的概念**,寫法-將 Route 包在 Route 中
`<Outlet />`:根據URL顯示對應的頁面
```
//index.js
  <BrowserRouter>
    <Routes>
      <Route path="/" element={<App />}>    //App裡面的是巢狀路由
        <Route index element={<Home />} />
        <Route path="products/:id" element={<Product />} />
        <Route path="checkout" element={<Checkout />} />
        <Route path="thankyou" element={<ThankYou />} />
        <Route path="profile" element={<Profile />} />
        <Route path="*" element={<Navigate to="/" replace />} />    //包裝useNevigate的Component,可傳props
      </Route>
    </Routes>
  </BrowserRouter>
```
```
//App.js
    <>
      <Reset />
      <GlobalStyle />
      <AuthContextProvider>
        <CartContextProvider>
          <Header />
          <Outlet />    //根據URL顯示對應的頁面,並共用Header/Footer
          <Footer />
        </CartContextProvider>
      </AuthContextProvider>
    </>
```
#### Link
`<Link />`功能類似`<a href="">`
戴上一個 `to` 屬性並設定與 Route 中 `Path` 屬性相同名稱就可以成功到該頁面了,to屬性可接收兩種參數:
1. String
```
<Link to="/">Home</Link>
```
2. Object: 代表著 window.location屬性
```
<Link to={{
    pathname: '/',    //同string
    search: '?q=ray',    //http://example/?q=ray#app
    hash: '#app'
  }}
  state={{    //透過 state 的參數在切換頁面時，就可以將前一頁面的資料往下一頁面傳遞
    products: {
      id: '1',
      name: 'QQ 產品'
    }
  }}>>Home</Link>
```
```
//取得 state 的資料
const { state } = useLocation();
  
useEffect(() => {
  console.log(state);    // { products: { id: '1', name: 'QQ 產品' } }
},[]);
```
#### NavLink
`<NavLink />`:進階版的 `<Link />`,可以追蹤你當前的頁面並給予相對應的狀態(class會多一個active,可搭配css使用)
![](https://i.imgur.com/TUBkFC6.gif)
```
//自訂active class名稱
<NavLink
  to="/"
  className={({ isActive }) => isActive ? 'router-link-active' : null }
  >
  Home
</NavLink>

//本來已有class名稱,自訂active class名稱
<NavLink
  to="/"
  className={({ isActive }) => 
    [
      'border p-3 hover:bg-indigo-600 duration-500',
      isActive ? 'router-link-active' : null
    ].join(' ')
  }
>
  Home
</NavLink>
```
#### Hook
* useParams
useSearchParams:取得?後面的內容
sample code-取得URL的id
```
//index.js
<Route path="products/:id" element={<Product />} />

//product.js
import { useParams } from 'react-router-dom';

function Product() {
  const [product, setProduct] = useState();
  const { id } = useParams();    //取得動態路由的id

  useEffect(() => {
    async function getProduct() {
      const { data } = await api.getProduct(id);
      setProduct(data);
    }
    getProduct();
  }, [id]);

  if (!product) return null;

  return (
  ...
  );
 }
```
* useLocation
```
import { useLocation } from "react-router";

const { hash, key, pathname, search, state } = useLocation();
  
useEffect(() => {
  console.log('hash:', hash); // hash: #products
  console.log('key:', key); // key: ry5x4mjc
  console.log('pathname:', pathname); // pathname: /products
  console.log('search:', search); // search: ?q=ray
  console.log('state:', state); // state: { products: { id: '1', name: 'QQ 產品' } }
},[]);
```
* useNavigate 用於重新導向,傳入字串也是可以正常運作的 `() => navigate('1')`、`() => navigate('-1')`
```
const navigate = useNavigate();

return(
   <button
        className="border-1 bg-indigo-500 p-3 text-white"
        onClick={ () => navigate('/products/123')}
      >
        點我跳轉
      </button>
      <button
        className="border-1 bg-indigo-500 p-3 text-white"
        onClick={ () => navigate(-1) }
      >
        前一頁
      </button>
      <button
        className="border-1 bg-indigo-500 p-3 text-white"
        onClick={ () => navigate(1) }
      >
        後一頁
      </button>
);
```
* useRoutes 讓整體程式碼更簡潔
```
//先建立一個專門管理 Router 的檔案
import { useRoutes } from 'react-router-dom';

import App from '../App';
import ToDoList from "../TodoList";

import Admin from "../admin/Index";
import AdminProducts from "../admin/Products";


const routes = [
  {
    path: '/',
    element: <App />,
    children: [
      {
        path: '/todoList',
        element: <ToDoList />,
      },
    ],
  },
  {
    path: '/admin',
    element: <Admin />,
    children: [
      {
        path: 'products',
        element: <AdminProducts />,
      },
    ],
  }
];

export default () => useRoutes(routes);
```
```
//index.js
import React from 'react'
import { Route, Routes, Link } from "react-router-dom";
import ReactDOM from 'react-dom/client'
import './index.css'

import Router from './router';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <Router />
  </React.StrictMode>
)
```
### 7. How to use styled-components to style components? What's the benefit of using it?
* 像CSS一樣寫style
```
const Nav = styled.div`
    ...
`
//等價

const Nav = styled("div")`
    ...
`
```
* 可傳props
* 可繼承&繼承並更改屬性
```
//繼承並更改屬性
const Nav = styled.div`
    ...
`

//用as=""更改

return(
<Nav
  as="button"
  />
  );
```
* 可以用在`<Link>` Component
* 保留我們自己寫的className
```
import styled, { withTheme } from "styled-components/macro";
```
* 變數前面$字號：給styled component的props
```
<Campaign
  $isActive={index === activeCampaignIndex}
  $backgroundImageUrl={picture}
  key={index}
  to={`/products/${product_id}`}
>
```
* 用&來加pseudo class
* 獨立styled component命名:`Component.styled.js` 可以知道是給哪個component的style
### Did you find any part in my code which is interesting and you want to share with others? Did you find any part which you think you can improve or do it by different way?
1. 有bug,加入不同商品,若顏色和尺寸相同會被當成同一個商品(後加入的數量和庫存會覆蓋已加入的,但下拉選單數量選擇依據是本來的品項)
![](https://i.imgur.com/QXXKlY3.png)
2. 在cart改動產品數量時,會變成字串相加