從 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
函式裡面:
- 先把
.todos
清空 - 最後用
.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 的程式碼
只要點擊 h1
,Counter
的 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 一次