重點整理
- 畫面永遠由 state 產生
- useState 的使用
- controlled component 與 uncontrolled component
- 修改用
map()
,刪除用filter()
,新增用解構語法
初探 state(以 TodoList 為例)
畫面永遠由 state 產生:UI = f(state)
新增 todo --> 改資料 --> 畫面更新
- 在 function component 裡面使用 hook:useState
STEP 1:從 React 物件中取出 useState 方法
STEP 2:呼叫 useState 方法後可以取得一個「變數(todos)」和「改變該變數的方法(setTodos)」
import { useState } from 'react'
function App() {
const [todos, setTodos] = useState(0) // 傳入初始值
// 解構語法
// function useState() {
// return [123, 456] <-- value
// }
const handleButtonClick = () => {
setCounter(counter + 1)
}
return (
<button onClick={handleButtonClick}>Add Todo</button>
) // 把 return 的東西放到畫面上,這個動作稱為 mount (放到 DOM 上)
}
- 在 JSX 裡面沒辦法用迴圈,使用
map()
的範例
function App() {
const [todos, setTodos] = React.useState([])
const handleButtonClick = () => {
setCounter(counter + 1)
}
return (
<button onClick={handleButtonClick}>Add Todo</button>
{
todos.map((todo, index) => < TodoItem key={index} content={todo} />)
}
)
}
- 重要觀念:state 為 immutable
function App() {
const [todos, setTodos] = React.useState([
123, 456, 555
])
const handleButtonClick = () => {
setTodos([...todos, Math.random()])
} // 不能去直接改 state,而是要另外產生一個 state
return (
<button onClick={handleButtonClick}>Add Todo</button>
{
todos.map((todo, index) => < TodoItem key={index} content={todo} />)
}
)
}
重要觀念:controlled component 與 uncontrolled component
所有在 UI 上會動的東西,幾乎都是個 state。controlled 和 uncontrolled 指的是「資料受不受到 React 所控制」,也就是「受 React 所控制的資料(controlled)」或「不受 React 所控制的資料(uncontrolled)」。
controlled component
function App() { const [value, setValue] = useState('') const handleInputChange = (e) => { setValue(e.target.value) } return ( <input type="text" placeholder="todo" value={value} onChange={handleInputChange} /> ) }
uncontrolled component & useRef
import { useRef } from 'react' function App() { const [value, setValue] = useState('') const inputRef = useRef(); const handleInputChange = (e) => { setValue(e.target.value) } return ( <input ref={inputRef} type="text" placeholder="todo" /> ) }
state 新增狀態:id
let id = 2
function App() {
const [todos, setTodos] = React.useState([
{ id: 1, content: 123
}
])
const handleButtonClick = () => {
setTodos([...todos, {
id,
content: value
})
setValue('')
id ++
}
return (
<button onClick={handleButtonClick}>Add Todo</button>
{
todos.map((todo) => < TodoItem key={todo.id} content={todo.content} />)
}
)
}
- useRef 新增 id 版本(不常使用)
import { useRef } from 'react'
function App() {
const [todos, setTodos] = React.useState([
{ id: 1, content: 123, isDone: ture }
{ id: 2, content: not done, isDone: false }
])
const [value, setValue] = useState('')
const idRef = useRef(3);
const handleButtonClick = () => {
setTodos([...todos, {
id: id.current
content: value
})
setValue('')
id.current ++
}
const handleToggleIsDone = id => {
setTodoes(todo.map(todo => {
if(todo.id !== id) return todo
return {
...todo,
isDone: !todo.isDone
}
}))
}
const handleDeleteTodo = id => { <-- 要刪掉的 id
setTodos(todo.filter(todo => todo.id !== id))
}
}
未完成 & 已完成的功能
@
TodoItem.js
const TodoContent = styled.div` ${props => props.size === 'XL' && ` font-suze: 20px; `} ${props => props.$isDone && ` text-decoration: line-through; `} export default function TodoItem( { className, size, todo, handleDeleteTodo }) { return( <TodoContent $isDone={todo.isDone} size={size}>{todo.content}</TodoContent> // 只要是給 styled-component 的屬性,會加上 $ 字號 ) }
@
App.js
function App() { onst handleToggleIsDone = id => { setTodoes(todo.map(todo => { if(todo.id !== id) return todo return { ...todo, isDone: !todo.isDone } })) } }
- 另外補充:Transient props
要給 styled-component 的屬性,加上 $ 字號
If you want to prevent props meant to be consumed by styled components from being passed to the underlying React node or rendered to the DOM element, you can prefix the prop name with a dollar sign ($), turning it into a transient prop.
In this example, $draggable isn't rendered to the DOM like draggable is.
(來自官方資料)
const Comp = styled.div`
color: ${props =>
props.$draggable || 'black'};
`;
render(
<Comp $draggable="red" draggable="true">
Drag me!
</Comp>
);