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一样没有使用的状态也依然存在无法注销。