开发调试 #
Redux DevTools 集成 #
Zustand 可以与 Redux DevTools 浏览器扩展集成,提供强大的调试功能。
基本配置 #
tsx
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}),
{ name: 'CounterStore' }
)
)
安装 Redux DevTools #
- Chrome: Redux DevTools Extension
- Firefox: Redux DevTools
- Edge: Redux DevTools
DevTools 配置选项 #
tsx
devtools(
(set) => ({ /* store */ }),
{
name: 'MyStore', // Store 名称
enabled: true, // 是否启用
anonymousActionType: '更新', // 匿名 action 名称
store: 'my-store', // DevTools 中的 store 名称
trace: true, // 启用调用栈追踪
traceLimit: 25, // 追踪深度
}
)
Action 命名 #
方式一:使用第三个参数 #
tsx
const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set(
(state) => ({ count: state.count + 1 }),
false,
'increment' // action 名称
),
add: (amount: number) => set(
(state) => ({ count: state.count + amount }),
false,
`add/${amount}` // 动态名称
),
}),
{ name: 'CounterStore' }
)
)
方式二:使用对象 #
tsx
const useStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set(
(state) => ({ count: state.count + 1 }),
false,
{ type: 'INCREMENT', payload: 1 }
),
add: (amount: number) => set(
(state) => ({ count: state.count + amount }),
false,
{ type: 'ADD', payload: amount }
),
}),
{ name: 'CounterStore' }
)
)
多个 Store #
tsx
const useCounterStore = create(
devtools(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}),
{ name: 'CounterStore' }
)
)
const useUserStore = create(
devtools(
(set) => ({
user: null,
setUser: (user) => set({ user }),
}),
{ name: 'UserStore' }
)
)
日志调试 #
自定义日志中间件 #
tsx
const log = (config) => (set, get, api) =>
config(
(args) => {
console.group('State Update')
console.log('%c Previous State:', 'color: #9E9E9E', get())
set(args)
console.log('%c Next State:', 'color: #4CAF50', get())
console.groupEnd()
},
get,
api
)
const useStore = create(
log((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
带时间戳的日志 #
tsx
const timestampLog = (config) => (set, get, api) =>
config(
(args) => {
const timestamp = new Date().toISOString()
console.log(`[${timestamp}] State Update:`, {
prev: get(),
next: typeof args === 'function' ? args(get()) : args,
})
set(args)
},
get,
api
)
条件日志 #
tsx
const conditionalLog = (shouldLog = () => true) => (config) => (set, get, api) =>
config(
(args) => {
if (shouldLog(get(), args)) {
console.log('State Update:', get(), '->', args)
}
set(args)
},
get,
api
)
// 只在开发环境记录
const useStore = create(
conditionalLog(() => process.env.NODE_ENV === 'development')(
(set) => ({ /* store */ })
)
)
状态快照 #
保存状态快照 #
tsx
const useStore = create((set, get) => ({
count: 0,
history: [] as number[],
increment: () => set((state) => ({
count: state.count + 1,
history: [...state.history, state.count]
})),
undo: () => set((state) => {
const lastState = state.history[state.history.length - 1]
return {
count: lastState,
history: state.history.slice(0, -1)
}
}),
}))
时间旅行调试 #
tsx
interface State {
count: number
snapshots: number[]
currentIndex: number
increment: () => void
saveSnapshot: () => void
travelTo: (index: number) => void
}
const useStore = create<State>((set, get) => ({
count: 0,
snapshots: [0],
currentIndex: 0,
increment: () => {
set((state) => ({ count: state.count + 1 }))
get().saveSnapshot()
},
saveSnapshot: () => set((state) => ({
snapshots: [...state.snapshots.slice(0, state.currentIndex + 1), state.count],
currentIndex: state.snapshots.length,
})),
travelTo: (index) => set((state) => ({
count: state.snapshots[index],
currentIndex: index,
})),
}))
React DevTools #
使用 React DevTools 检查 #
tsx
import { useEffect } from 'react'
function DebugStore() {
const state = useStore()
useEffect(() => {
console.log('Store State:', state)
}, [state])
return null
}
// 在 App 中使用
function App() {
return (
<>
<DebugStore />
{/* 其他组件 */}
</>
)
}
自定义 Hook 调试 #
tsx
function useDebugStore(selector, name = 'Store') {
const state = useStore(selector)
useEffect(() => {
console.log(`[${name}] State:`, state)
}, [state, name])
return state
}
// 使用
function Component() {
const count = useDebugStore((state) => state.count, 'Counter')
return <div>{count}</div>
}
控制台调试 #
在控制台中访问 Store #
tsx
// 将 store 暴露到全局
if (typeof window !== 'undefined') {
(window as any).__STORE__ = useStore
}
// 在控制台中使用
// __STORE__.getState() // 获取状态
// __STORE__.setState({}) // 设置状态
// __STORE__.subscribe() // 订阅变化
创建调试工具函数 #
tsx
const createDebugTools = (store: any, name: string) => {
const tools = {
name,
getState: store.getState,
setState: store.setState,
subscribe: store.subscribe,
log: () => console.log(`[${name}]`, store.getState()),
reset: () => store.setState(store.getInitialState?.() || {}),
time: (fn: () => void) => {
console.time(name)
fn()
console.timeEnd(name)
},
profile: (fn: () => void) => {
console.profile(name)
fn()
console.profileEnd(name)
},
}
if (typeof window !== 'undefined') {
(window as any)[`__${name.toUpperCase()}__`] = tools
}
return tools
}
// 使用
createDebugTools(useStore, 'counter')
// 在控制台
// __COUNTER__.log()
// __COUNTER__.reset()
错误追踪 #
错误边界 #
tsx
import { Component, ReactNode } from 'react'
interface Props {
children: ReactNode
store: any
}
interface State {
hasError: boolean
error: Error | null
}
class StoreErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props)
this.state = { hasError: false, error: null }
}
static getDerivedStateFromError(error: Error) {
return { hasError: true, error }
}
componentDidCatch(error: Error, errorInfo: any) {
console.error('Store Error:', error)
console.error('State:', this.props.store.getState())
console.error('Component Stack:', errorInfo.componentStack)
}
render() {
if (this.state.hasError) {
return (
<div>
<h2>状态错误</h2>
<pre>{this.state.error?.message}</pre>
<button onClick={() => this.setState({ hasError: false, error: null })}>
重试
</button>
</div>
)
}
return this.props.children
}
}
// 使用
function App() {
return (
<StoreErrorBoundary store={useStore}>
<MyApp />
</StoreErrorBoundary>
)
}
状态验证 #
tsx
import { z } from 'zod'
const stateSchema = z.object({
count: z.number().min(0).max(100),
name: z.string(),
})
const validateState = (config) => (set, get, api) =>
config(
(args) => {
const newState = typeof args === 'function' ? args(get()) : args
const result = stateSchema.safeParse(newState)
if (!result.success) {
console.error('State validation failed:', result.error)
return
}
set(args)
},
get,
api
)
const useStore = create(
validateState((set) => ({
count: 0,
name: '',
increment: () => set((state) => ({ count: state.count + 1 })),
}))
)
性能调试 #
渲染追踪 #
tsx
const useRenderCount = (name: string) => {
const count = useRef(0)
count.current++
useEffect(() => {
console.log(`[${name}] Render count:`, count.current)
})
}
function Component() {
useRenderCount('MyComponent')
const count = useStore((state) => state.count)
return <div>{count}</div>
}
选择器性能 #
tsx
import { useEffect, useRef } from 'react'
function useDebugSelector(selector: (state: any) => any, name: string) {
const prevRef = useRef<any>()
const value = useStore(selector)
useEffect(() => {
if (prevRef.current !== value) {
console.log(`[${name}] Selector changed:`, {
prev: prevRef.current,
next: value,
})
}
prevRef.current = value
}, [value, name])
return value
}
更新频率监控 #
tsx
const updateMonitor = (config) => (set, get, api) => {
let updateCount = 0
let lastTime = Date.now()
return config(
(args) => {
updateCount++
const now = Date.now()
if (now - lastTime >= 1000) {
console.log(`Updates per second: ${updateCount}`)
updateCount = 0
lastTime = now
}
set(args)
},
get,
api
)
}
实际案例 #
完整的调试配置 #
tsx
import { create } from 'zustand'
import { devtools, persist, createJSONStorage } from 'zustand/middleware'
const isDev = process.env.NODE_ENV === 'development'
const useStore = create(
devtools(
persist(
(set, get) => ({
count: 0,
name: '',
increment: () => set(
(state) => ({ count: state.count + 1 }),
false,
'increment'
),
setName: (name: string) => set(
{ name },
false,
'setName'
),
reset: () => set(
{ count: 0, name: '' },
false,
'reset'
),
}),
{
name: 'app-storage',
storage: createJSONStorage(() => localStorage),
}
),
{
name: 'AppStore',
enabled: isDev,
trace: isDev,
}
)
)
// 开发环境暴露调试工具
if (isDev && typeof window !== 'undefined') {
(window as any).__STORE__ = {
getState: useStore.getState,
setState: useStore.setState,
subscribe: useStore.subscribe,
reset: () => useStore.setState({ count: 0, name: '' }),
}
}
最佳实践 #
1. 条件启用 DevTools #
tsx
devtools(
store,
{
name: 'MyStore',
enabled: process.env.NODE_ENV === 'development',
}
)
2. 为 Actions 命名 #
tsx
// ✅ 好:有意义的名称
set({ count: 0 }, false, 'reset')
// ❌ 不好:匿名更新
set({ count: 0 })
3. 使用错误边界 #
tsx
<StoreErrorBoundary store={useStore}>
<App />
</StoreErrorBoundary>
4. 生产环境移除调试代码 #
tsx
const log = process.env.NODE_ENV === 'development'
? (msg: string) => console.log(msg)
: () => {}
总结 #
开发调试的关键点:
- 使用 Redux DevTools 进行状态追踪
- 自定义日志中间件记录状态变化
- 使用错误边界捕获状态错误
- 在控制台暴露调试接口
- 监控性能和渲染频率
接下来,让我们学习 测试策略,掌握单元测试和集成测试技巧。
最后更新:2026-03-28