AppWorks School Batch #16 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。

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


#AppWorks School









Related Posts

現有建築光電發電投資的實質選擇權估值

現有建築光電發電投資的實質選擇權估值

[筆記] MySQL 環境安裝(Ubuntu 18.04)與 phpMyAdmin 架設

[筆記] MySQL 環境安裝(Ubuntu 18.04)與 phpMyAdmin 架設

if流程圖-規劃流程

if流程圖-規劃流程


Comments