ref() 開始看在 Vue 中,會使用 ref() 記錄可能改變的資料。
例如 TodoForm.vue 使用 ref() 記錄輸入框目前的文字:
<script setup>
import { ref } from 'vue'
const text = ref('')
</script>
這裡傳入的空字串:
ref('')
是 text 一開始的值。
在 <script> 中取得或修改這個值時,需要使用 .value:
console.log(text.value)
text.value = '學習 React'
送出表單後,專案也是透過 .value 清空內容:
text.value = ''
看到 React 專案時,原本的 ref() 不見了,改成了:
const [text, setText] = useState('')
第一眼很容易直接把它們想成:
Vue 的 ref() = React 的 useState()
但它們真的可以直接畫上等號嗎?
useState() 是什麼?useState() 是 React 提供的 Hook,可以讓 Component 記住一個 State。
目前 TodoForm.tsx 中使用它記錄輸入框的文字:
import { useState } from 'react'
function TodoForm({ onAdd }: TodoFormProps) {
const [text, setText] = useState('')
// ...
}
和 Vue 的 ref('') 一樣,傳入的空字串是初始值:
useState('')
不同的是,useState() 會回傳一個包含兩個值的陣列:
const [text, setText] = useState('')
這兩個值分別是:
text:目前的 StatesetText:更新 State 的函式可以先理解成:
目前的值 更新值的函式
↓ ↓
[text, setText]
React 官方文件中常見的基本寫法也是:
const [state, setState] = useState(initialState)
useState() 回傳的是一個陣列,因此這裡使用了 JavaScript 的陣列解構:
const [text, setText] = useState('')
如果不使用解構,可以先想成:
const result = useState('')
const text = result[0]
const setText = result[1]
使用陣列解構後,就可以直接把兩個值取出:
const [text, setText] = useState('')
text 和 setText 並不是 React 規定的固定名稱,但通常會按照這種方式命名:
const [something, setSomething] = useState(initialValue)
例如:
const [name, setName] = useState('')
const [count, setCount] = useState(0)
const [completed, setCompleted] = useState(false)
目前專案則使用:
const [text, setText] = useState('')
從名稱就能看出 text 是目前的文字,而 setText 負責更新文字。
Vue 在 JavaScript 中讀取 ref() 的值時,需要使用:
text.value
React 則可以直接使用 text:
console.log(text)
在 JSX 中也可以直接將它放進輸入框:
<input
type="text"
value={text}
placeholder="輸入待辦事項..."
/>
所以兩者讀取資料的方式不同:
// Vue
text.value
// React
text
Vue 可以透過 .value 直接指定新的值:
text.value = ''
React 則要呼叫 useState() 提供的更新函式:
setText('')
目前 TodoForm.tsx 在送出表單後,就是這樣清空輸入框:
onAdd(trimmed)
setText('')
輸入內容發生變化時,也會使用 setText() 更新:
onChange={(event) => setText(event.target.value)}
當 setText() 收到新的文字後,React 會保存新的 State,重新 Render Component,並更新畫面。
因此 React 不是這樣更新:
text = ''
而是這樣:
setText('')
可以這樣理解:
text負責提供目前的值,setText()負責通知 React 更新這個值。
useState()除了輸入框的文字,App.tsx 也使用 useState() 記錄所有待辦事項:
const [todos, setTodos] = useState<Todo[]>([])
這裡的初始值是空陣列:
[]
而兩個解構出來的值分別是:
todos:目前的待辦事項陣列setTodos:更新待辦事項陣列的函式新增待辦事項時,專案會呼叫:
setTodos([...todos, newTodo])
刪除待辦事項時則會呼叫:
setTodos(todos.filter((todo) => todo.id !== id))
雖然傳入的新資料不同,但概念和 setText('') 一樣,都是透過 useState() 提供的函式更新 State。
偷偷說,useState<Todo[]>([]) 這段看起來很難懂、很混亂對叭但沒關係不用緊張因為我也還在熟悉中<Todo[]> 是 TypeScript 的型別寫法,等後面進到 TypeScript 的部分時再仔細看。這裡先將重點放在 useState() 的用途,就不深入說明。
ref() 和 useState() 真的一樣嗎?這邊一樣將兩個專案的寫法放在一起比較:
// Vue
const text = ref('')
// React
const [text, setText] = useState('')
兩者的用途確實很接近:
但實際使用方式並不相同:
Vue ref() |
React useState() |
|---|---|
| 回傳一個 Ref 物件 | 回傳一個包含兩個值的陣列 |
在 JavaScript 中使用 .value 讀取 |
直接使用 State 變數讀取 |
使用 .value = 新值 更新 |
使用 setState(新值) 更新 |
例如 text.value = '' |
例如 setText('') |
如果只看「記錄會改變的資料」這個用途,ref() 和 useState() 的確很像。
但它們不是完全相同的 API,也不能直接把 Vue 的寫法原封不動地搬到 React。
useState() 是 React 提供的 Hook,讓 Component 記住一個 StateuseState(initialValue) 會回傳一個陣列,通常用 [something, setSomething] 解構出目前的值與更新函式.value
setState() 函式,呼叫後 React 會重新 Render Component一開始會覺得:
ref() 換成 useState() 就好了吧?
實際比較後才發現,兩者雖然都能記錄會改變的資料,但使用方式還是有明顯差異。Vue 把值放在 .value 裡,而 React 則把目前的值和更新它的函式一起交給我。這個中括號看久之後,好像也慢慢沒有一開始那麼陌生了。
那麼,下篇見!