先別急著學 React


Posted by saffran on 2021-02-25

從 Todo list 開始吧!

實作「新增、刪除、標示已完成/未完成」的功能

react 的第一個重要概念:Component

Component 就是「元件、組件」
component 的 function 名稱用大寫開頭,較好辨別它是一個 component

資料 vs 畫面

如果在操作 todo 時,「同時更新」資料和畫面,當其中一個忘記更新時,就會發生「資料、畫面不一致」的問題

要解決此問題,有兩種方式:

方式一

操作 todo 時,只更新畫面
當需要資料的時候,再把資料從畫面上拿出來

這樣,資料和畫面永遠都會是「同步」的

方式二(react 就是這個概念)

操作 todo 時,只更新資料
「畫面」都是由資料產生

這樣,資料和畫面永遠都會是「同步」的

react 的第二個重要概念:在 react,畫面永遠由 state 產生

state 就是「資料」 (程式的狀態 = 程式的資料)

用比較數學的寫法就是:

UI = f(state)

畫面永遠都由 state 產生

首先,先定義好一個 state

id 不算是 state,因為別人只需要知道 todo 的 data-id 是多少就好了,不需要知道我自己這邊的 id 是多少

  let state = {
    todos: []
  }

做任何操作時,只改變資料,畫面都先不用動

render 函式就是負責「把 state 轉成 UI」

宣告一個 render 函式,在 render 函式裡面:

  1. 先把 .todos 清空
  2. 最後用 .join('') 把每個 todo 的 html 給結合起來
  // 把 state 轉成 UI
  function render() {
    $('.todos').empty(); // 先把 .todos 清空
    $('.todos').append(
      state.todos.map(todo => {return Todo(todo)}).join('')
    )
  }

因為每當我改變 state 時,就要呼叫 render 函式,所以就可以這樣寫:

  function updateState(newState) {
    state = newState;
    render();
  }

初探 React

{} 裡面可以寫 JavaScript 的程式碼,例如下面的 {props.name}

import React from "react";
import ReactDOM from "react-dom";

function Love(props) {
  return <h1>I love you, {props.name}</h1>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Love name="Harry"/>, rootElement);

把參數 props 改為解構的寫法:

import React from "react";
import ReactDOM from "react-dom";

function Love({ name }) {
  return <h1>I love you, {name}</h1>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Love name="Harry"/>, rootElement);

傳其他的參數進去,像是下面的 n

可以寫成 n={5} 或是 n="5"
不可以寫成 n=5

import React from "react";
import ReactDOM from "react-dom";

function Love({ name, n }) {
  return <h1> {n} I love you, {name}</h1>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<Love name="Harry" n={5}/>, rootElement);

放在 <Love></Love> 標籤裡面的內容,會被當作是 children 這個參數傳進去 function component 裡面

children 這個參數是 react 幫我做好的

import React from "react";
import ReactDOM from "react-dom";

function Love({ children }) {
  return <h1>I love you {children}</h1>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(
  <Love>
    for a thousand year
  </Love>, rootElement);

state

React.useState() 是一個 function,會回傳給我一個陣列,陣列裡面:

  • 第一個是我的值 value
  • 第二個是我可以去設定它 setValue

React.useState(10) 小括號裡面填的是「初始值」

Counter() 是一個 component

  • 這個 component 的狀態就是 value,初始值是 10
  • 要改狀態的時候,我只需要呼叫 setValue 即可
import React from "react";
import ReactDOM from "react-dom";

function Counter() {
  const [value, setValue] = React.useState(10);
  return <h1>{value}</h1>
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Counter />, rootElement);

在 component 加上 event listener

onClick={handleClick} 在大括號裡面要寫的是 JS 的程式碼
只要點擊 h1Counter 的 state 就會 +1

import React from "react";
import ReactDOM from "react-dom";

function Counter() {
  const [value, setValue] = React.useState(10);
  function handleClick() {
    setValue(value + 1)
  }
  return <h1 onClick={handleClick}>{value}</h1>
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <Counter />, rootElement);

return 後面如果要換行,就要用小括號包起來

return 後面如果直接接上東西,就不需要小括號,例如:

return <h1 onClick={handleClick}>{value}</h1>

return 後面如果要換行,
錯誤寫法:return 後面沒有接東西,就會是預設的 return undefined

function Title({ size }) {
  if (size === 'XL') {
    return <h1>Hello</h1>
  }
  return
    <h2 style={{
      color: 'red',
      textAlign: 'center'
    }}>Hello</h2>

}

正確寫法:要 return 的東西要用小括號包起來,才能換行

function Title({ size }) {
  if (size === 'XL') {
    return <h1>Hello</h1>
  }
  return (
    <h2 style={{
      color: 'red',
      textAlign: 'center'
    }}>Hello</h2>
  )
}

react 的 render 指的是:

Counter 這個 component 處理完,把 Counter return 的東西,放到畫面上

當 state 更新時,react 會重新呼叫一次 Counter 這個 component,並重新 render 一次


#React







Related Posts

[Day 7] JS in Pipeline (7): CI/CD pipeline (2) (Fin.)

[Day 7] JS in Pipeline (7): CI/CD pipeline (2) (Fin.)

第十四週概要

第十四週概要

Context API 效能問題 - use-context-selector 解析

Context API 效能問題 - use-context-selector 解析


Comments