AppWorks School Front-End Class 學習筆記&心得(駐點階段三:個人專案前準備~Dive into topics)


Posted by ralphhong5465 on 2022-09-14

在進入駐點集訓的重頭戲-個人專案前,有一週名為「Dive into topics」的技術鑽研時間,練習 React 的進階觀念如 styled-components、React Router、useReducer + Context 等,同時一邊發想個人專案主題,可說是整個培訓下半場的暖身活動。

進度安排

為期一週的技術補強時間,生活跟 STYLiSH 時期有些相似,一樣要寫 School 安排好的作業、一樣會在 GitHub 上發 PR 給導師審閱、一樣會在通過後合併再同步到本機,每天早上也都有晨會(recap),除討論作業進度外,也針對個人專案即將使用的 create-react-app 開發環境做了初步簡介。

因為後面有更重要的個人專案,本週一共只安排兩個階段的進度,比 STYLiSH 每天都有新進度輕鬆許多,導師也強調要以調整好自己的步調、完整構思個人專案為優先。當然,如果兩階段的進度提早完成,也可以私下去找導師,挑戰隱藏版的第三、甚至第四階段任務。

經過十二週的學習(含前四週遠距階段),School 在本週週六安排期中考,讓大家檢視自己的學習狀況。期中考以遠距方式進行,可以用各種方式找答案,但不應與人討論。如果前面學得夠紮實,題目本身並不算太難,但如果寫得不順或成果不理想也不必太擔心,考試結果不會影響畢業或後續進度,再找時間把不熟的觀念補起來就行。

CSS in JS 重點(以 styled-components 為例)

  1. 簡介:把 CSS 寫在 React 元件裡,有別於從其他檔案引入 CSS 的方式。
  2. 好處:讓每個 HTML 元素 CSS 的 class 都變得獨一無二,避免全域汙染。
  3. 範例:
// 最上面先引入styled-components套件
import styled from 'styled-components';

// 建立Button的styled元件,名稱第一個字母應大寫
// 把相關的CSS設定寫在裡面
const Button = styled.button`
  width:150px;
  height:80px;
  color:white;
  border-radius:1rem;

  ${'' /* 動態props寫法
  可讓單一元件可因props不同套用不同屬性 */}
  background-color: ${(props) => 
    props.align === "left" ? "red" : "blue"};

  ${'' /* 偽類(pseudo-class)寫法 */}
  &:hover{
    cursor:pointer;
    background-color:black;
  }

  ${'' /* 做RWD套用media query寫法 */}
  @media (max-width: 1000px) {
    width:100px;
    height:53px;
    font-size:0.6rem;
  }  
`

function App() {
  return (
    <>
      {/* 在JSX裡把建好的styled元件當一般元件使用 */}
      <Button align={"left"}>Primary Button</Button>
      <Button>Secondary Button</Button>
    </>
  );
}

export default App;

上述程式碼呈現畫面如下:

若在檢測器中的 DOM 觀察這兩顆按鈕,可發現兩者皆被創造一個獨一無二的 class:

補充:模組化 CSS(CSS modules)亦將每個 CSS 的 class 都加上一個雜湊(hash),也可避免全域汙染。

React Router 重點

  1. 功能:實現單面應用(single-page application, SPA),僅載入一個 .html 頁面,避免更改網址就重新發送請求、接收回應,提升使用者體驗。

基本範例:

//App()

import { BrowserRouter, Route, Routes } from "react-router-dom";
import Main from "./Main";
import Info from "./Info";

function App() {
  return (
    <>
      <BrowserRouter>
        <Routes>
          {/* 設定'/'路徑到<Main />頁、'/info'路徑到<Info />頁 */}
          <Route path='/' element={<Main />} />
          <Route path='/info' element={<Info />} />    
        </Routes>
      </BrowserRouter>
    </>
  );
}

export default App;

另建立 Main 頁面如下:

//Main()

import React from 'react';

const Main = () => {
  return (
    <>
      <h1>Main</h1>
    </>
  )
}

export default Main;

Info 頁面如下:

//Info()

import React from 'react';

const Info = () => {
  return (
    <>
      <h1>
        Info
      </h1>
    </>
  )
}

export default Info;

瀏覽器顯示結果如下:

搭配 Navigate

//App()

import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
import Main from "./Main";
import Info from "./Info";

function App() {
  return (
    <>
      <BrowserRouter>
        <Routes>
          {/* 所有非設定路徑都透過Navigate導引到首頁 */}
          <Route path="*" element={<Navigate to="/" />} />
          <Route path='/' element={<Main />} />
          <Route path='/info' element={<Info />} />    
        </Routes>
      </BrowserRouter>
    </>
  );
}

export default App;

瀏覽器顯示結果如下,可發現如果輸入非設定好的網址都會被導引到首頁:

動態路由 & useParams

//App()

import { BrowserRouter, Route, Routes } from "react-router-dom";
import Item from "./Item";

function App() {
  return (
    <>
      <BrowserRouter>
        <Routes>
        {/* 設定動態路由
        // 可另設定路徑為為item/123 */}
          <Route path='/item/:itemId' element={<Item />} />   
        </Routes>
      </BrowserRouter>
    </>
  );
}

export default App;

另建立 Item 頁面如下:

//Item()

import { useParams } from "react-router-dom";

const Item = () => {
  // 取得網址的params供下方JSX使用
  const params = useParams();
  const itemId = params.itemId;

  return (
    <>
      <h1>
        Item ID is {itemId}
      </h1>   
    </>
  )
}

export default Item;

瀏覽器顯示結果如下:

透過 Link 或 useNavaigate 建立超連結

App() 不變,但將 Item() 改成如下:

//Item()

import { useParams, Link, useNavigate } from "react-router-dom";

const Item = () => {
  const params = useParams();
  const itemId = params.itemId;

  // 取用useNavigate功能
  const navigate = useNavigate();

return (
    <>
      <h1>
        Item ID is {itemId}
      </h1>

      {/* 點擊此Link可抵達Info頁面 */}
      <Link to="/info">Go to Info</Link>

      {/* 透過navigate,點擊此div可抵達首頁  */}
      <div onClick={() => {navigate(`/`);}}>
        Go back to Homepage
      </div>   
    </>
  )
}

export default Item;

在瀏覽器可以發現,不論點擊 Link 或綁定 navigate 的 <div>,都可以有超連結效果,且都可以在換頁後點擊「上一頁」回到原頁面:

巢狀路由 & Outlet

與動態路由相似,都可以在 params 後面加上下一層的 params,Outlet 可標示該頁專屬的內容顯示區。

現將 App() 改成如下:

//App()

import { BrowserRouter, Route, Routes, Navigate } from "react-router-dom";
import Main from "./Main";
import Profile from "./Profile";

function App() {
  return (
    <>
      <BrowserRouter>
        <Routes>
          <Route path="*" element={<Navigate to="/" />} />
          <Route path='/' element={<Main />} />
          {/* 設定巢狀路由*/}
          <Route path='/profile/*' element={<Profile />} >
            {/* 路徑為'/profile/categoryA'時,
            Profile頁面另顯示<p>Category A</p> */}
            <Route path="categoryA" element={<p>Category A</p>} />
            {/* 路徑為'/profile/categoryA'時,
            Profile頁面另顯示<p>Category B</p> */}
            <Route path="categoryB" element={<p>Category B</p>} />
          </Route>  
        </Routes>
      </BrowserRouter>
    </>
  );
}

export default App;

在 Profile() 中的程式碼中,可透過 Outlet 標示在 App() 中,element 的顯示位置:

//Profile()

import React from 'react';
import { Outlet } from "react-router-dom";

const Profile = () => {
  return (
    <>
      <h1>Profile</h1>
      <Outlet />
    </>
  )
}

export default Profile;

瀏覽器顯示結果如下,可發現如果 params 只有 profile 時,頁面也僅有寫在 Profile() 的內容,但若有下一層的 params 且該層有設定 element,則 element 內容會顯示在 <Outlet /> 位置:

useReducer + Context 重點

  1. 功能:在跨元件同時管理多個狀態(state)時,將兩者結合可將所有狀態綜合管理,並確保與狀態無關的元件不需要作為中介者傳遞特定狀態。

useReducer(簡單狀態,取自 Codevolution 教學影片

import React, { useReducer } from 'react';

const initialState = 0;
const reducer = (state, action) => {
  switch(action){
    case 'increment':
      return state + 1
    case 'decrement':
      return state - 1
    case 'reset':
      return initialState
    default:
     return state
  }
};

function App() {
  const [count, dispatch] = useReducer(reducer, initialState)

return (
    <>
      <div>Count is {count}</div>
      <button onClick={() => dispatch('increment')}> 
        Increment
      </button>
      <button onClick={() => dispatch('decrement')}> 
        Decrement
      </button>
      <button onClick={() => dispatch('reset')}>
        Reset
      </button>
    </>
  );
}

export default App;

在上述範例中,可分析 useReducer 運作步驟如下:

  1. useReducer 取得 reducer 函式與原始狀態 initialStateuseReducer(reducer, initialState)
  2. 設定 reducer 函式與原始狀態 initialState
// 原始狀態為0
const initialState = 0;

// reducer函式取state與action兩參數
// 若action === 'increment'則state+1
// 若action === 'decrement'則state-1
// 若action === 'reset'則變回initialState
// 若無action則為既有的state
const reducer = (state, action) => {
  switch(action){
    case 'increment':
      return state + 1
    case 'decrement':
      return state - 1
    case 'reset':
      return initialState
    default:
     return state
  }
};
  1. useReducer 以 count 表示當前狀態,可顯示在 JSX 中、dispatch 代表對應不同情況時,會指派何種 action:const [count, dispatch] = useReducer(reducer, initialState)
  2. JSX 中可顯示當下 count 狀態、dispatch 告訴 reducer 函式當下對應的是哪一種 case

       {/* 這裡顯示當下的count狀態 */}
       <div>Count is {count}</div>
    
       {/* 點擊Increment按鈕表示對應reducer函式中increment的case */}
       <button onClick={() => dispatch('increment')}>
         Increment
       </button>
       {/* 點擊Decrement按鈕表示對應reducer函式中decrement的case */}
       <button onClick={() => dispatch('decrement')}>
         Decrement
       </button>
       {/* 點擊Reset按鈕表示對應reducer函式中reset的case */}
       <button onClick={() => dispatch('reset')}>
         Reset
       </button>
    

    瀏覽器顯示結果如下:

useReducer(複雜狀態,修改自 Codevolution 教學影片

在上述範例中,useReducer 做到的事也可以用 useState 完成,尚無法彰顯 useReducer「處理複雜狀態」的優勢,因此,在下述範例中,我們透過兩種方式讓狀態變得複雜:

  1. count 狀態變為一物件,原本的狀態取名為 firstCounter ,另新增 count 中第二個名為 secondCounter 的狀態。
  2. 新增按鈕專門改變 secondCounter 的狀態。
  3. 不管是對 firstCountersecondCounter ,都有新的按鈕,點擊時會對該狀態加上/減去不同數字。
import React, { useReducer } from 'react';

// 原始狀態變為一物件
// 內有firstCounter與secondCounter
const initialState = {
  firstCounter:0,
  secondCounter:10
};

// action變為物件,取當中的type表示不同case
const reducer = (state, action) => {
  switch(action.type){
    //未含2的兩種case僅改變count狀態的firstCounter
    case 'increment':
      return {...state, firstCounter:state.firstCounter + action.value}
    case 'decrement':
      return {...state, firstCounter:state.firstCounter - action.value}
    //包含2的兩種case僅改變count狀態的secondCounter
    case 'increment2':
      return {...state, secondCounter:state.secondCounter + action.value}
    case 'decrement2':
      return {...state, secondCounter:state.secondCounter - action.value}
    case 'reset':
      return initialState
    default:
     return state
  }
};

function App() {
  const [count, dispatch] = useReducer(reducer, initialState)

return (
    <>
      {/* count變為物件,再各取當中要顯示的值 */}
      <div>First Count is {count.firstCounter}</div>
      <div>Second Count is {count.secondCounter}</div>

      {/* dispatch的action都變為物件,以便同時設定兩種狀態 */}
      <button onClick={() => dispatch({type:'increment', value:1})}>
        Increment first counter by 1
      </button>
      <button onClick={() => dispatch({type:'decrement', value:2})}>
        Decrement first counter by 2
      </button>
      <button onClick={() => dispatch({type:'increment', value:3})}>
        Increment first counter by 3
      </button>
      <button onClick={() => dispatch({type:'decrement', value:4})}>
        Decrement first counter by 4
      </button>

      <br />

      {/* 針對狀態中的secondCounter,再寫不同的dispatch */}
      <button onClick={() => dispatch({type:'increment2', value:5})}>
        Increment second counter by 5
      </button>
      <button onClick={() => dispatch({type:'decrement2', value:6})}>
        Decrement second counter by 6
      </button>
      <button onClick={() => dispatch({type:'increment2', value:7})}>
        Increment second counter by 7
      </button>
      <button onClick={() => dispatch({type:'decrement2', value:8})}>
        Decrement second counter by 8
      </button>

      <br />

      <button onClick={() => dispatch({type:'reset'})}>
        Reset
      </button>
    </>
  );
}

export default App;

瀏覽器畫面如下:

Context

Context / Context API 的主要用途就是讓資料可以跨元件傳輸,避免 prop drilling 問題。

以下述範例來說,資料在 App() 提供、ChildTwo() 取用,如果只用 props 傳這些資料,中間不會用到該筆資料、僅用來傳遞訊息的 ChildOne() 就成了工具人,這就是 prop drilling 現象;使用 Context 可讓資料直接從 App() 傳到 ChildTwo(),讓介在中間的 ChildOne() 可保持乾淨、不接觸到該筆資料。

左方未使用 Context 的情況即為 prop drilling,有元件僅為傳遞資料的工具人

使用 Context 主要有三大步驟:

  1. 製造一個 Context:
//Example()

import React from 'react';

// 創造一個Context
const ExampleContext = React.createContext();

// 輸出Context
export default ExampleContext;
  1. 在所有會使用到該 Context 的地方最上層包覆 Provider,並寫好要傳出的值:
//App()

import ChildOne from "./ChildOne";
import ExampleContext from "./ExampleContext";

const App = () => {
  return (
    <>
      {/* 將會用到Context的地方包覆Provider,並寫好傳出的值 */}
      <ExampleContext.Provider value={{
        name:'Andy',
        age:50
      }}>
        <ChildOne />
      </ExampleContext.Provider>
    </>
  )
}

export default App;
  1. 在要取用該 Context 的地方包覆 Consumer,並用一回呼函式取得傳入的值:
//ChildTwo()

import ExampleContext from "./ExampleContext";

const ChildTwo = () => {
  return (
    <div style={{backgroundColor:"yellow"}}>
      <h1>Child Two</h1>
      {/* 要接收值的地方包Consumer */}
      <ExampleContext.Consumer>
        {/* 用回呼函式取得傳入的值 */}
        {(ctx) => {
          return (
            <>
              <div>My name is {ctx.name}.</div>
              <div>I am {ctx.age} years old.</div>
            </>
          )
        }}
      </ExampleContext.Consumer>
    </div>
  )
}

export default ChildTwo;

在 Consumer 的地方,也可以用 useContext 這個 hook,就可以避免複雜的回呼函式:

//ChildTwo()

import { useContext } from "react";
import ExampleContext from "./ExampleContext";

const ChildTwo = () => {
  // 用useContext取得傳入的值,供下方JSX使用
  const ctx = useContext(ExampleContext);

  return (
    <div style={{backgroundColor:"yellow"}}>
    <h1>Child Two</h1>
      {/* 不需再有回呼函式 */}
      <div>My name is {ctx.name}.</div>
      <div>I am {ctx.age} years old.</div>
    </div>
  )
}

export default ChildTwo;

若觀察夾在 App() 與 ChildTwo() 中間的 ChildOne() 元件,可發現當中完全不會被 Context 影響:

//ChildOne();

import ChildTwo from "./ChildTwo";

const ChildOne = () => {
  return (
    <div style={{backgroundColor:"pink"}}>
      <h1>Child One</h1>
      <ChildTwo />
    </div>
  )
}

export default ChildOne;

不論在接收處是用 Consumer 或 useContext 接住傳入內容,瀏覽器看到的畫面皆如下,粉紅色區域為與 Context 無關的 ChildOne() 元件,黃色區域為接收 Context 資訊的 ChildTwo() 元件:

useReducer + Context(修改自 Codevolution 教學影片

現在我們將 useReducer 與 Context 結合,目標為在不同元件中管理同一個狀態。

現有一最上層元件 App(),我們欲在 ChildTwo() 與 ChildThree() 元件同時管理一個名為 count 的狀態,但 ChildTwo() 與 App() 之間隔有一層 ChildOne() 元件,該元件與 count 狀態無關。

首先,先在App() 元件寫好 count 狀態與 dispatch,再透過 Provider 往其他地方傳:

//App()

import React, { useReducer } from 'react';
import ChildOne from "./ChildOne";
import ChildThree from "./ChildThree";

// 在App()元件建立好CountContext
export const CountContext = React.createContext();

// initialState與reducer皆先制定,再透過CountContext往其他元件傳
const initialState = 0;
const reducer = (state,action) => {
  switch(action){
    case 'increment':
      return state + 1
    case 'decrement':
      return state - 1
    case 'reset':
      return initialState
    default:
      return state
  }
}

const App = () => {
  // 用useReducer建立好count狀態與dispatch
  const [count, dispatch] = useReducer(reducer, initialState)

  return (
    <>
      {/* count狀態與dispatch透過Provider往下傳 */}
      <CountContext.Provider value={{
        countState: count,
        countDispatch: dispatch
      }}>
        <h1>Count in App is {count}</h1>
        <ChildOne />
        <ChildThree />
      </CountContext.Provider>
    </>
  )
}

export default App;

ChildOne() 元件不受影響,因此仍為如下程式碼:

//ChildOne();

import ChildTwo from "./ChildTwo";

const ChildOne = () => {
  return (
    <div style={{backgroundColor:"pink"}}>
      <h1>Child One</h1>
      <ChildTwo />
    </div>
  )
}

export default ChildOne;

ChildTwo() 與 ChildThree() 元件皆會取用 count 狀態與 dispatch ,兩者的程式碼相似。

ChildTwo() 程式碼如下:

//ChildTwo

import { useContext } from "react";
import { CountContext } from "./App";

const ChildTwo = () => {
  // 用useContext取得傳入的值,賦值於countContext變數供下方JSX使用
  const countContext = useContext(CountContext);

  return (
    <div style={{backgroundColor:"yellow"}}>
    {/* 透過countContext取得當中的countState狀態 */}
    <h1>Child Two: Count is {countContext.countState}</h1>
      {/* 透過countContext取得當中的countDispatch方法 */}
      <button onClick={() => countContext.countDispatch('increment')}>
        Increment
      </button>
      <button onClick={() => countContext.countDispatch('decrement')}>
        Decrement
      </button>
      <button onClick={() => countContext.countDispatch('reset')}>
        Reset
      </button>
    </div>
  )
}

export default ChildTwo;

ChildThree() 程式碼如下:

//ChildThree()

import { useContext } from "react";
import { CountContext } from "./App";

const ChildThree = () => {
  // 用useContext取得傳入的值,賦值於countContext變數供下方JSX使用
  const countContext = useContext(CountContext);

  return (
    <div style={{backgroundColor:"aqua"}}>
    {/* 透過countContext取得當中的countState狀態 */}
    <h1>Child Three: Count is {countContext.countState}</h1>
      {/* 透過countContext取得當中的countDispatch方法 */}
      <button onClick={() => countContext.countDispatch('increment')}>
        Increment
      </button>
      <button onClick={() => countContext.countDispatch('decrement')}>
        Decrement
      </button>
      <button onClick={() => countContext.countDispatch('reset')}>
        Reset
      </button>
    </div>
  )
}

export default ChildThree;

如此一來,就可以在 ChildTwo() 與 ChildThree() 元件同時透過 dispatch 管理 count 狀態:

Redux 重點

  1. 官方定義:JavaScript 應用程式中,可預測的狀態容器。(A Predictable State Container for JS Apps)。
  2. 功能:與 useReducer + Context 類似,可跨元件同時管理多個狀態。
  3. 原理:資料流為單向,流程說明取官網圖示如下:


Redux 原理示意圖(取自 Redux 官網

初始設定

  1. 以 reducer 函式建立一個 store,reducer 函式初次回傳的值即為初始的 state
  2. 使用者介面(UI)元件訂閱(subscribe)並渲染當下 store 中的 state,上圖範例為 $0。

發生異動(如上圖中點擊 Deposit $10 按鈕後)

  1. Event Handler 指派(dispatch)一個 action 物件到 store,以上圖而言,action 物件為 {type:"deposit", payload:10}
  2. Reducer 函式取原 $0 的 state 與新傳入的 action 進行運算,運算結果即為新的 state ,上圖範例為 $10。
  3. Store 通知所有 UI 元件說 store 已被更新。
  4. 每個 UI 元件檢查自己的 state 是否因該次 store 更新而受影響,若有則會依新的 state 重新渲染畫面,如上圖中的 UI 變為 $10。

範例(以原生 JavaScript 撰寫,修改自 React — The Complete Guide (incl Hooks, React Router, Redux) 線上課程提供之範例):

//redux-demo.js

//先引入redux套件
const redux = require('redux');

//建立一個store中的reducer函式
// 函式要取兩個參數:
// 1.初始狀態{counter:0}
// 2.發生事件後由dispatch()傳入的action物件
const counterReducer = (state = {counter:0}, action) => {

// action物件中的type為'increment'時,讓state中的counter加上1
    if(action.type === 'increment'){
        return {
            counter:state.counter + 1
        };
    }

// action物件中的type為'decrement'時,讓state中的counter減去1
    if(action.type === 'decrement'){
        return {
            counter:state.counter - 1
        };
    }

// 經action對應的type更新state後,reducer回傳新的state值
    return state;    
};

// 建立一個store,store裡面存一個reducer函式
const store = redux.legacy_createStore(counterReducer);

// 以store中的getState方法拿到最新的state
const counterSubscriber = () => {
    const latestState = store.getState();
    console.log(latestState)
}

// 取store的subscribe方法,以counterSubscriber函式為引數
// state改變時就執行subscribe()
store.subscribe(counterSubscriber);

// 設定store兩種dispatch()方法
// dispatch()傳到counterReducer的action物件
// 會透過type決定要如何改變state
store.dispatch({type:'increment'});
store.dispatch({type:'decrement'});

在命令提示字元輸入 node redux-demo.js 即可看到執行結果:

理解方式如下:

//原始的state為{counter:0}

//第一次dispatch傳入的type為increment
//帶回reducer函式後,會對應讓state中的counter加上1的狀況
{ counter: 1 }

//第二次dispatch傳入的type為decrement
//帶回reducer函式後,會對應讓state中的counter減去1的狀況
{ counter: 0 }

由於撰寫 Redux 程式碼有一定複雜度且容易出錯,一般會改用 Redux Toolkit (RTK) 處理,詳細可參考 Codevolution 製作的系列教學影片

為了便於操作,在用 React 搭配 Redux 或 RTK 進行開發時,多會搭配 React-Redux 套件,但值得注意的是,不論是 Redux 或 RTK,都非 React 限定工具,可搭配其他框架如 AngularJS、Vue.js、甚至是原生 JavaScript 一起使用(如上述範例即使用原生 JavaScript 實現)。

補充:zustand

說明:與 Redux 同為可供 React 使用的狀態管理函式庫,寫起來比 Redux 更為清爽,詳細使用方式可見官方 GitHub 說明

zustand 官方圖示(取自 GitHub

心得

在上個週末端午連假前,校長 Shirney 跟負責輔導就業的 Tiffany 各出了一份作業給我們,前者讓我們開始構思個人專案主題、後者則讓我們上網看職缺,思考自己的職涯方向。

原來個人專案不是我當初想的那樣

在遠距學習時期,就曾在「AppWorks School 三月女性校友線上分享會|成為妳心目中的軟體工程師」線上直播活動中,聽到任職於 LINE TV 的學姐 Aimee 強調個人專案的重要性,我也在遠距第一週作業就開始試著用 HTML+CSS 刻出當時想像的個人專案模樣,但經過數週駐點集訓的洗禮後,卻赫然發現當初想的跟我現階段實際要呈現的有非常大的不同。

首先是就開發工具而言,我們不再用純 HTML+CSS+JavaScript 寫網頁,而是用前端函式庫 React,畢竟現在業界也不太有人會只用前端三劍客進行開發;再者,以謀職為導向的個人專案作品著重「技術」的呈現,思考網頁功能時,除了從使用者體驗的角度來看之外,還多少摻雜一些為了使用到特定技術的設計,要特別留意的是,倒也不用為了秀技術而做跟本身專案主題扯不上邊的功能。

學習、學習、還是學習

以 React 的各種 hook 而言,最常用的莫過於 useState 跟 useEffect,前者多用來處理事件、後者處理跟畫面呈現不直接相關的副作用(side effect),目前碰到的專案多半只需要使用到這兩者,偶爾操作 DOM 元素會另外用到 useRef。然而,React 還有不少的 hook,只是以目前的專案複雜度而言,都還派不上用場,因此,我特別珍惜本週學習 useReducer + Context 的機會,並同步認識與之觀念相仿的 Redux。

這幾週以來陸續看到上一屆的學長姐開始面試,看著他們的生活,也不免想到兩個月後的自己。在完成 Tiffany 所派作業的過程中,我們可以看到各家公司對於求職者能力的要求,這才發現過去數週完成專案時所學的技術有多麼重要,還有更多業界普遍使用、但目前還不會的技能,例如 TypeScript、Next.js、Jest 等,想學的還有很多,但要在接下來這五週就把所有不會的技能補齊顯得有些不切實際,還是以「把個人專案做好」為第一要務,待專案告一段落後,也別忘了要持續學習,畢竟軟體疊代速率很快,而 School 最重視的,就是我們的學習能力。


#AppWorks School









Related Posts

Rails API 學習紀錄 (Part 1)

Rails API 學習紀錄 (Part 1)

Slide In on Scroll

Slide In on Scroll

NLP Data Augmentation 常見方法

NLP Data Augmentation 常見方法


Comments