状态管理库之 zustand

感觉跟 pinia 有点像…

在这里插入图片描述

一、基本使用

1.1 编写、组织代码

  • store

    • appleStore.ts
    const { create } from 'zustand'
    type AppleStateType = {
        price:number,
        count:number,
        increment:()=>void,
        decremnent:()=>void,
        getTotal:()=>number
    }
    const useAppleStore = create<AppleStateType>()((set,get)=>({
         // 状态
         price:7.0,
         count:10,
         //动作
         increment(){
             set(state =>({
                 ...state,
                 count:state.count + 1
             }))
         },
         decrement(){
             set(state=>({
                 ...state,
                 count:state.count - 1
             }))
         },
         getTotal(){
             return get().price * get().count
         }
    }))
    export default useAppleStore
    
  • 第一层不需要解构

  • set 中传入箭头函数,箭头函数返回一个对象,set 为设置状态

  • get 为获取当前状态

  • ts 语法中在泛型后面加上一个()

1.2 使用

  • App.tsx
import useAppStore from './store/appleStore'

const Child1 = ()=>{
    const price = useAppleStore(state=>state.price)
    const count = useAppleStore(state=>state.count)
    const increment = useAppleStore(state=>state.increment)
    const decrement = useAppleStore(state=>state.decrement)
    const getTotal = useAppleStore(state=>state.getTotal)
    
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>数量:{count}</h2>
        <h3>金额:{getTotal()}</h3>
        <button onClick={decrement}>-1</button>
        <button onClick={increment}>+1</button> 
       </>
    )
}

const App = ()=>{
    return(
    	<Child1 />
    )
}

二、异步

2.1 编写、组织代码

  • store

    • appleStore.ts
    const { create } from 'zustand'
    type AppleStateType = {
        price:number,
        count:number,
        increment:()=>void,
        decremnent:()=>void,
        getTotal:()=>number,
        doubleCount:()=>Promise<undefined>
    }
    const useAppleStore = create<AppleStateType>()((set,get)=>({
         // 状态
         price:7.0,
         count:10,
         //动作
         increment(){
         },
         decrement(){
         },
         getTotal(){
         },
        async doubleCount(){
            const rate = await Promise.resolve(2)
            set(state=>({
                ...state,
                count:state.count * rate
            }))
        }
    }))
    export default useAppleStore
    

2.2 使用

  • App.tsx
import useAppStore from './store/appleStore'

const Child1 = ()=>{
    const price = useAppleStore(state=>state.price)
    const count = useAppleStore(state=>state.count)
    const increment = useAppleStore(state=>state.increment)
    const decrement = useAppleStore(state=>state.decrement)
    const getTotal = useAppleStore(state=>state.getTotal)
    const doubleCount = useAppleStore(state=>state.doubleCount)
    
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>数量:{count}</h2>
        <h3>金额:{getTotal()}</h3>
        <button onClick={decrement}>-1</button>
        <button onClick={increment}>+1</button> 
        <button onClick={doubleCount}>*2</button> 
       </>
    )
}

const App = ()=>{
    return(
    	<Child1 />
    )
}

三、immer 中间件

3.1 安装

  • 安装:npm i immer

3.2 使用

  • store

    • appleStore.ts
      • 包裹 create 后面
      • set 中返回代码块,不是对象
      • 使用 immer 之后直接覆盖就行,不需要重新创造一个对象
    const { create } from 'zustand'
    import {immer} from 'zustand/middleware/immer'
    type AppleStateType = {
        price:number,
        count:number,
        increment:()=>void,
        decremnent:()=>void,
        getTotal:()=>number,
        doubleCount:()=>Promise<undefined>
    }
    const useAppleStore = create<AppleStateType>()(immer(
        (set,get)=>({
             // 状态
             price:7.0,
             count:10,
             //动作
             increment(){
                 set(state=>{state.count += 1})
             },
             decrement(){
                 set(state=>{state.count -= 1})
             },
             getTotal(){
             },
            async doubleCount(){
                const rate = await Promise.resolve(2)
                set(state=>{count *= rate})
            }
        })
    ))
    export default useAppleStore
    

四、useShallow

4.1 编写代码

  • store

    • appleStore.ts
    const { create } from 'zustand'
    import {immer} from 'zustand/middleware/immer'
    type AppleStateType = {
        price:number,
        count:number,
        color:string,
        increment:()=>void,
        decremnent:()=>void,
        getTotal:()=>number,
        doubleCount:()=>Promise<undefined>
    }
    const useAppleStore = create<AppleStateType>()(immer(
        (set,get)=>({
             // 状态
             price:7.0,
             count:10,
             color:red,
             //动作
             increment(){
                 set(state=>{state.count += 1})
             },
             decrement(){
                 set(state=>{state.count -= 1})
             },
             getTotal(){
             },
            async doubleCount(){
                const rate = await Promise.resolve(2)
                set(state=>{count *= rate})
            }
        })
    ))
    export default useAppleStore
    

4.2 使用

  • App.tsx
    • 注意:const {price,color} = useAppleStore()
      • 表示获取到了每一项,虽然只解构出来了 price 和 color
        • 意味着,在 Child1 组件中对 store 中的 count 等其他属性进行刷新,那么 Child2 组件也会进行刷新
import useAppStore from './store/appleStore'

const Child2 = ()=>{
    const {price,color} = useAppleStore()
    
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>颜色:{color}</h2>
       </>
    )
}

const App = ()=>{
    return(
    	<Child2 />
    )
}
  • App.tsx
    • 让 useAppleStore 传入一个箭头函数,箭头函数返回一个对象
    • 使用 useShallow 包裹箭头函数
import {useShallow} from 'zustand/react/shallow'
import useAppStore from './store/appleStore'

const Child2 = ()=>{
    const {price,color} = useAppleStore(useShallow(state=>({
        price:state.price,
        color:state.color
    })))
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>颜色:{color}</h2>
       </>
    )
}

const App = ()=>{
    return(
    	<Child2 />
    )
}

五、调试工具

5.1 前提

  • 安装扩展插件:redux-devtools

5.2 使用

  • store

    • appleStore.ts
      • 使用 devtools 进行包裹
        • 第二个参数为配置选项
          • 能否被观察
          • 名称定义
    const { create } from 'zustand'
    import {immer} from 'zustand/middleware/immer'
    import {devtools} from 'zustand/middleware/immer'
    type AppleStateType = {
        price:number,
        count:number,
        color:string,
        increment:()=>void,
        decremnent:()=>void,
        getTotal:()=>number,
        doubleCount:()=>Promise<undefined>
    }
    const useAppleStore = create<AppleStateType>()(immer(
        devtools(
            (set,get)=>({
                 // 状态
                 price:7.0,
                 count:10,
                 color:red,
                 //动作
                 increment(){
                     set(state=>{state.count += 1})
                 },
                 decrement(){
                     set(state=>{state.count -= 1})
                 },
                 getTotal(){
                 },
                async doubleCount(){
                    const rate = await Promise.resolve(2)
                    set(state=>{count *= rate})
                }
            })
        ,{enabled:true,name:'Apple Store'})
    ))
    export default useAppleStore
    

六、持久化存储

6.1 中间件的顺序

  • immer
    • devtools
      • 参数
        • 回调:返回一个状态
        • 配置对象
          • 默认情况下存储到 localStorage
          • name 设置 key
      • subscribeWithSelector
        • persist(set、get 的回调,{name:‘…’})

6.2 全部持久化存储

  • store

    • appleStore.ts
    const { create } from 'zustand'
    import {immer,persist} from 'zustand/middleware/immer'
    import {devtools} from 'zustand/middleware/immer'
    type AppleStateType = {
    }
    const useAppleStore = create<AppleStateType>()(immer(
        devtools(
         persist(
            (set,get)=>({
                 // 状态
                 price:7.0,
                 count:10,
                 color:red,
                 //动作
                 increment(){
                     set(state=>{state.count += 1})
                 },
                 decrement(){
                     set(state=>{state.count -= 1})
                 },
                 getTotal(){
                 },
                async doubleCount(){
                    const rate = await Promise.resolve(2)
                    set(state=>{count *= rate})
                }
            })
         ,{name:'myAllpeStore'})
        ,{enabled:true,name:'Apple Store'})
    ))
    export default useAppleStore
    

6.3 仅存储指定属性

  • 配置选项中增加 partialize:(state)=>({price:state.price})

    • 返回哪个状态就保存哪一个状态
    • 只对 price 进行持久化保存
  • store

    • appleStore.ts
    const { create } from 'zustand'
    import {immer,persist} from 'zustand/middleware/immer'
    import {devtools} from 'zustand/middleware/immer'
    type AppleStateType = {
    }
    const useAppleStore = create<AppleStateType>()(immer(
        devtools(
         persist(
            (set,get)=>({
                 // 状态
                 price:7.0,
                 count:10,
                 color:red,
                 //动作
                 increment(){
                     set(state=>{state.count += 1})
                 },
                 decrement(){
                     set(state=>{state.count -= 1})
                 },
                 getTotal(){
                 },
                async doubleCount(){
                    const rate = await Promise.resolve(2)
                    set(state=>{count *= rate})
                }
            })
         ,{name:'myAllpeStore',partialize:(state)=>({price:state.price})})
        ,{enabled:true,name:'Apple Store'})
    ))
    export default useAppleStore
    

6.4 指定某些属性不存储

  • 仍然借助 partialize

    • 对应箭头函数返回一个对象
      • fromEntries:将 [ [key,value] ] 变成对象的形式 { key:value}
        • entries:将对象的 key 和 value 整合到一个元组中,再将所有的元组加入到数组中进行返回(二维数组)
  • store

    • appleStore.ts
      • 指定不要 price
    const { create } from 'zustand'
    import {immer,persist} from 'zustand/middleware/immer'
    import {devtools} from 'zustand/middleware/immer'
    type AppleStateType = {
    }
    const useAppleStore = create<AppleStateType>()(immer(
        devtools(
         persist(
            (set,get)=>({
            })
         ,{name:'myAllpeStore',partialize:(state)=>
          	Object.fromEntries(
            	Object.entries(state).filter(([key])=>!['price'].includes(key))
            )
          })
        ,{enabled:true,name:'Apple Store'})
    ))
    export default useAppleStore
    

6.5 指定存储位置

  • 在配置选项中进行配置

  • store

    • appleStore.ts
    const { create } from 'zustand'
    import {immer,persist} from 'zustand/middleware/immer'
    import {devtools} from 'zustand/middleware/immer'
    type AppleStateType = {
    }
    const useAppleStore = create<AppleStateType>()(immer(
        devtools(
         persist(
            (set,get)=>({
            })
         ,{name:'myAllpeStore',partialize:(state)=>
          	Object.fromEntries(
            	Object.entries(state).filter(([key])=>!['price'].includes(key))
            ),storage:createJSONStorage(()=>sessionStorage)
          })
        ,{enabled:true,name:'Apple Store'})
    ))
    export default useAppleStore
    

七、subscribe

7.1 基本使用

  • App.tsx
    • 问题所在
      • 因为 Child2 组件中使用了 count,因此当 Child1 中更新了 count 之后,Child2 会重新渲染
      • Child2 依赖于 count 的值来进行渲染判断,但是其实没有必要每次都渲染的,因为 count 的值还是 < 7,直接返回上一次的"有点少"就够了
import {useShallow} from 'zustand/react/shallow'
import useAppStore from './store/appleStore'

const Child2 = ()=>{
    const {price,color} = useAppleStore(useShallow(state=>({
        price:state.price,
        color:state.color,
        count:state.count
    })))
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>颜色:{color}</h2>
        <h1>{count > 7 :'很多':'有点少'}</h1>
       </>
    )
}

const App = ()=>{
    return(
    	<Child2 />
    )
}
  • 解决方案:使用订阅机制
    • 会执行代码,但是不会让组件二重新渲染
    • 要求只订阅一次:使用 useEffect
    • 定义变量 text 设置文字
import {useShallow} from 'zustand/react/shallow'
import {useEffect} from 'react'
import useAppStore from './store/appleStore'

const Child2 = ()=>{
    const [text,setText] = useState('有点少')
    const {price,color} = useAppleStore(useShallow(state=>({
        price:state.price,
        color:state.color,
    })))
    useEffect(()=>{
       const cancelSubscribe = useAppleStore.subscribe((newState,prevState)=>{
        console.log('状态改变——这里执行')
        if(state.count >= 7 && prevState.count < 7){
            setText('很多')
        }else if(state.count < 7 && prevState.count >= 7){
            setText('太少')
        }
    })
       return cancelSubscribe
    },[])
   
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>颜色:{color}</h2>
        <h1>{text}</h1>
       </>
    )
}

const App = ()=>{
    return(
    	<Child2 />
    )
}

7.2 监听指定状态

  • 实现颗粒度更小的监听

  • 需要添加中间件:subscribeWithSelector

  • store

    • appleStore.ts
    const { create } from 'zustand'
    import {immer,persist,subscribeWithSelector} from 'zustand/middleware/immer'
    import {devtools} from 'zustand/middleware/immer'
    type AppleStateType = {
    }
    const useAppleStore = create<AppleStateType>()(immer(
        devtools(
            subscribeWithSelector(
            	 persist(
                    (set,get)=>({
                    })
                 ,{name:'myAllpeStore',partialize:(state)=>
                    Object.fromEntries(
                        Object.entries(state).filter(([key])=>!['price'].includes(key))
                    )
                  })
            )
        ,{enabled:true,name:'Apple Store'})
    ))
    export default useAppleStore
    
  • App.tsx

    • 开始订阅的时候也要执行一次
    • 设置变量
import {useShallow} from 'zustand/react/shallow'
import {useEffect} from 'react'
import useAppStore from './store/appleStore'

const Child2 = ()=>{
    const [text,setText] = useState('有点少')
    const {price,color} = useAppleStore(useShallow(state=>({
        price:state.price,
        color:state.color,
    })))
    useEffect(()=>{
       const cancelSubscribe = useAppleStore.subscribe(
       	state=>state.count,
        (count,prevCount)=>{
            if(count >= 7 && (prevCount < 7 || count === prevCount)){
                setText('很多')
            }else if(count < 7 && (prevCount >= 7 || count === prevCount)){
                setText('太少')
            }
        },
        { equalityFn:shallow,
          fireImmediately:true
        }
       )
       return cancelSubscribe
    },[])
   
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>颜色:{color}</h2>
        <h1>{text}</h1>
       </>
    )
}

const App = ()=>{
    return(
    	<Child2 />
    )
}

八、getState、setState

8.1 使用场景

  • 只需要在一个组件中使用修改状态的动作,不需要其他组件实现,不放在统一的位置

8.2 使用

  • App.tsx
import useAppStore,{decrement,increment,getTotal,doubleCount} from './store/appleStore'

const Child1 = ()=>{
    const price = useAppleStore(state=>state.price)
    const count = useAppleStore(state=>state.count)
    // const increment = useAppleStore(state=>state.increment)
    //const decrement = useAppleStore(state=>state.decrement)
   // const getTotal = useAppleStore(state=>state.getTotal)
   // const doubleCount = useAppleStore(state=>state.doubleCount)
    const myLocalAction = ()=>{
        useAppStore.setState((state)=>{state.count += 5})
    }
    return(
    	<>
        <h1>单价:{price}</h1>
        <h2>数量:{count}</h2>
        <h3>金额:{getTotal()}</h3>
        <button onClick={decrement}>-1</button>
        <button onClick={increment}>+1</button> 
        <button onClick={doubleCount}>*2</button> 
		<button onClick={myLocalAction}>Child1 独有的方法</button>
       </>
    )
}

const App = ()=>{
    return(
    	<Child1 />
    )
}

8.3 简化代码

  • 将动作写在外边
const { create } from 'zustand'
import {immer,persist} from 'zustand/middleware/immer'
import {devtools} from 'zustand/middleware/immer'
type AppleStateType = {
}
const useAppleStore = create<AppleStateType>()(immer(
    devtools(
     persist(
        (set,get)=>({
        })
     ,{name:'myAllpeStore',partialize:(state)=>
      	Object.fromEntries(
        	Object.entries(state).filter(([key])=>!['price'].includes(key))
        ),storage:createJSONStorage(()=>sessionStorage)
      })
    ,{enabled:true,name:'Apple Store'})
))
export default useAppleStore
export const increment =()=>{
    useAppleStore.setState((state)=>{state.count+=1})
}
export const decrement =()=>{
    useAppleStore.setState((state)=>{state.count-=1})
}
export const getTotal =()=>{
    return useAppleStore.getState().price * useAppleStore.getState().count
}
export const doubleCount = async ()=>{
    const rate = await Promise.resolve(2)
    useAppleStore.setState((state)=>{state.count *= rate})
}
  • 26
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值