Props Drilling
- 直接來看範例
function DemoInnerBoxContent({ setTitle }) {
return (
<div>
<button
onClick={() => {
setTitle(Math.random())
}}
>Update title!</button>
</div>
)
}
function DemoInnrerBox({ setTitle }) {
return <DemoInnerBoxContext setTitle={setTitle} />
}
function DemoInner({ setTitle }) {
return <DemoInnerContext setTitle={setTitle} />
}
export default function Demo() {
const [title, setTitle] = useState("I am title!")
return (
<div>
titile: {title}
<DemoInner setTitle={setTitle} />
</div>
)
}
useContext
解決 Props Drilling 的問題。仍要達到父層想要傳遞資料、函式到子層的目的。
- 引入
{ useContext, createContext }
- 在要提供資料的父層,加上
<TitleContext.Provider>
- 然後在裡面加上
{value={setTitle}}
,意思是把 setTilte 這個值傳下去 - 在目標子層,加入
const setTitle = useContext(TitleContext)
import { useContext, createContext } from "react"
const TitleContenxt = createContext()
const ColorContext = createContext()
function DemoInnerBoxContent() {
const setTitle = useContext(TitleContext)
const colors = useContext(ColorContext)
return (
<div>
<button
style={{
color: colors.primary
}}
onClick={() => {
setTitle(Math.random())
}}
>Update title!</button>
</div>
)
}
function DemoInnrerBox() {
return <DemoInnerBoxContext />
}
function DemoInner() {
return <DemoInnerContext />
}
export default function Demo() {
const [title, setTitle] = useState("I am title!")
const [colors, setColors] = useState({
primary: "#ff0000",
});
return (
<ColorContext.Provider value={{colors
}}>
<TitleContext.Provider {value={setTitle}}>
<div>
<button
onClick={() => {
setColors({
primary: "#00000F"
})
}}
>
titile: {title}
<DemoInner />
</div>
</TitleContext.Provider>
</ColoeContext.Provider>
)
}
總結:以資料為中心去思考
資料改變會需要改變畫面的,它就是 state
- 規劃 state
以 TodoList 為例:把畫面上的資料拿下來,變成一個 data;接著 data 可以幫你還原成原本畫面。
{
inputValue: 'abcde',
todos: [
{
isChecked: true,
content: 'ddw'
}, {
isChecked: false,
content: 'todo1'
}
],
itemLeftCount: 1, // 非必要,derived state
filter: active,
}
todoId: 2 // 需要改變但不影響畫面 useRef
State vs Derived State
Derived State 是什麼?可以透過現有的 State 組合/計算而成舉個 Derived State 的例子
export default function App() {
const [todos, setTodos] = useState ([
{content: '1', id:1, isChecked: true },
{content: '2', id:2 isChecked: false }
])
const [filter, setFilter] = useState('active');
const [value, setValue] = sueState('');
// 使用 useCallback()
const handleChange = React.useCallback((e) => {
setValue(e.target.value)
}, [todos])
// 沒有改 Todos、filter ,每次 re-render 都還是要計算一次
// 這時候 useMemo() 派上用場
const filteredTodos = useMemo(() =>
return todos.filter(todo => {
if (filter === 'all') return true
return filter === 'completed' ? todo.isChecked : !todo.isChecked
}, [todos, filter])
// 當 Todos、filter 改變的時候 filterTodos 會重算一次;反之,不會計算
}
)
return (
<div>
<input value={value} onChange={e => setValue(handleChange)} />
{filteredTodos.map((todo) => <div key={todo.id}>{todo.content}</div>))}
</div>
)