useReducer
useState
可以用最簡單的方式更新狀态,但是狀态更新的邏輯散落在UI中,不能獨立複用,也不便于測試。使用
useReducer
來對一個元件的狀态進行管理就可以避免這一情況。
import React, { useState, useReducer } from 'react'
const initialState = []
const reducer = (state, action) => {
const [type, value] = action
const copy = [...state]
switch (type) {
case 'add':
copy.push(value)
return copy
case 'del':
copy.splice(value, 1)
return copy
default:
return copy;
}
}
export default TodoList = () => {
const [state, dispatch] = useReducer(reducer, initialState)
const [value, setValue] = useState('')
return (
<div>
<h2>list</h2>
<input type="text" onBlur={(e) => setValue(e.target.value)} />
<button onClick={() => dispatch(['add', value])}>+</button>
<ul>
{
state.map((item, index) => (
<li key={index}>
<span>{index + 1}:{item}</span>
<button onClick={() => dispatch(['del', index])}>-</button>
</li>
))
}
</ul>
</div>
)
}
這樣我們就完成了一個簡單的TodoList元件
useContext
useReducer
雖然很好地分離了邏輯和UI,但是無法像redux一樣進行跨元件的狀态共享,在某些情況下如如果元件的狀态都使用redux進行管理會導緻redux比較混亂,是以針對比較複雜的元件我們使用
useContext
來實作跨元件的狀态共享可以很好的避免這些情況。
//index
import React, { useReducer } from 'react'
import Input from './input'
import List from './list'
export const TodoContext = React.createContext({})
const initialState = []
const reducer = (state, action) => {
const [type, value] = action
const copy = [...state]
switch (type) {
case 'add':
copy.push(value)
return copy
case 'del':
copy.splice(value, 1)
return copy
default:
return state;
}
}
export default () => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<div>
<h2>list</h2>
<p>Number:{state.length}</p>
//在頂層元件中下發state和dispatch
<TodoContext.Provider value={{ state, dispatch }}>
<Input />
<List />
</TodoContext.Provider>
</div>
)
}
//input
import React, { useContext, useState } from 'react'
import {TodoContext} from './index'
export default () => {
//下層元件使用useContext擷取頂層元件下發的state和dispatch
const {dispatch} = useContext(TodoContext)
const [value, setvalue] = useState('')
return (
<div>
<input type="text" onBlur={(e) => setvalue(e.target.value)} />
<button onClick={() => dispatch(['add', value])}>+</button>
</div>
)
}
//list
import React, { useContext } from 'react'
import Todo from './todo'
import { TodoContext } from './index'
export default () => {
//下層元件使用useContext擷取頂層元件下發的state和dispatch
const { state } = useContext(TodoContext)
return (
<ul>
{
state.map((item, index) => <Todo item={item} index={index} key={index} />)
}
</ul>
)
}
//todo
import React, { useContext } from 'react'
import { TodoContext } from './index'
export default ({ index, item }) => {
//下層元件使用useContext擷取頂層元件下發的state和dispatch
const {dispatch} = useContext(TodoContext)
return (
<li key={index}>
<span>{index + 1}:{item}</span>
<button onClick={() => dispatch(['del', index])}>-</button>
</li>
)
}
在元件頂層使用
useReducer
統一管理狀态和邏輯,下層元件隻負責UI部分,這樣元件的狀态管理就變得很清晰了。每一個元件都會有一套内部的狀态管理,元件登出時該元件的狀态也就登出了,不會像redux一樣沒有使用的狀态也依然存在無法登出。