Replies: 2 comments 9 replies
-
What difference between
and
|
Beta Was this translation helpful? Give feedback.
9 replies
-
This is my implementation if you need it // src/common/ts/store.tsx
import { createContext, useContext, FC, PropsWithChildren } from 'react'
import { action, AnnotationsMap } from 'mobx'
import { useLocalObservable as sourceUseLocalObservable } from 'mobx-react-lite'
import { isServer } from './utils'
import { MobxStateHydrationKey } from '@/types'
import type { DepthPartial, PickFunctionName } from '@/types'
type WithSetStore<CurrentStore> = CurrentStore & {
setStore: <S extends Omit<CurrentStore, PickFunctionName<CurrentStore>>, K extends keyof S>(
this: WithSetStore<CurrentStore>,
payload: (Pick<S, K> | S) | ((prevState: Readonly<S>) => (Pick<S, K> | S ))
) => void
}
/**
* 在原有useLocalObservable 增加setStore方法 及对应的类型校验
*/
export const useLocalObservable = <TStore extends Record<string, any>>(
initializer: () => TStore,
annotations?: AnnotationsMap<TStore, never>
) => {
return sourceUseLocalObservable<WithSetStore<TStore>>(
() =>
Object.assign(initializer(), {
setStore<S extends Omit<TStore, PickFunctionName<TStore>>, K extends keyof S>(
this: WithSetStore<TStore>,
payload: (Pick<S, K> | S) | ((prevState: Readonly<S>) => (Pick<S, K> | S ))
) {
const prevState = {} as S
for (const key in this) typeof this[key] !== 'function' && (prevState[key] = this[key])
const newState = typeof payload === 'function' ? payload(prevState) : payload
Object.assign(this, newState)
},
}),
annotations
)
}
export abstract class BaseStore<CurrentStore, RootStore = null> {
constructor(protected rootStore: RootStore) {} // 多个store通信
/**
* 借鉴了
* @desc [react setState](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365#issuecomment-351013257)
* @desc [pinia $patch](https://pinia.vuejs.org/core-concepts/state.html#usage-with-the-options-api)
* @example
* setStore({ count: 1 })
* setStore(state => ({ ...state, c: new C() }))
*/
@action.bound
setStore<S extends Omit<CurrentStore, PickFunctionName<CurrentStore>>, K extends keyof S> (payload: (Pick<S, K> | S) | ((prevState: Readonly<S>) => (Pick<S, K> | S ))) {
const prevState = {} as S
// @ts-expect-error
for (const key in this) typeof this[key] !== 'function' && (prevState[key] = this[key])
const newState = typeof payload === 'function' ? payload(prevState) : payload
Object.assign(this, newState)
}
}
type StoreLinkComOptions = {
isPage: boolean
}
const defaultStoreLinkComOptions: StoreLinkComOptions = {
isPage: true, // 页面级store 当组件重新渲染会重新创建一个store实例
}
/**
* 关联 store 与 组件 基于Context
*/
export const storeLinkCom = <
T extends new (...args: any) => InstanceType<T>
>(
Store: T,
options: StoreLinkComOptions = defaultStoreLinkComOptions
) => {
const _store = new Store()
const StoreContext = createContext(_store)
const useStore = () => {
const context = useContext(StoreContext)
// 其实这行代码永远不会触发 因为createContext初始化就有默认值_store
if (context === undefined) throw new Error(`useStore 必须在 StoreProvider 中使用,来自 ${Store.name}`)
return context
}
type StoreProviderProps = {
[MobxStateHydrationKey]?: DepthPartial<
Omit<typeof _store, PickFunctionName<typeof _store>>
>
}
const StoreProvider: FC<PropsWithChildren<StoreProviderProps>> = (props) => {
const store = _initializeStore(props[MobxStateHydrationKey])
return (
<StoreContext.Provider value={store}>
{props.children}
</StoreContext.Provider>
)
}
function _initializeStore(data: StoreProviderProps[typeof MobxStateHydrationKey]) {
// SSG和SSR总是创建一个新的 store 在客户端中(isPage true 每次创建新的 false 全局创建一次)store
const newStore = isServer ? new Store() : options.isPage ? new Store() : _store
// 如果您的页面有使用 Mobx存储的 Next 数据获取方法,那么它将 在这里 hydrate
// @ts-expect-error
if (data) newStore?.setStore(data)
return newStore
}
return {
useStore,
StoreProvider,
}
}
// 递归 Partial
export type DepthPartial<O> = O extends object ? {
[key in keyof O]?: DepthPartial<O[key]>
} : O
// 获取一个对象的函数名的联合类型
export type PickFunctionName<T> = { [P in keyof T]: T[P] extends Function ? P : never; }[keyof T]; // src/pages/testMobx/store.ts
import { observable, computed, makeObservable } from 'mobx'
import { BaseStore, storeLinkCom } from '@/common/ts'
import { GetUserQuery } from '@/types'
class TestMobxStore extends BaseStore<TestMobxStore> {
constructor() {
super(null)
makeObservable(this)
}
@observable
articleTitle = ''
@observable
articleContent = ''
@observable
getUserQuery: null | GetUserQuery = null
@computed
get articleContentLength() {
return this.articleContent.length
}
}
export default TestMobxStore
const { useStore, StoreProvider } = storeLinkCom(TestMobxStore)
export { useStore, StoreProvider } |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
First of all, I would like to thank everyone for their contribution to this project and thank you for taking time out of your busy schedule to see my thoughts
I am a heavy Mobx user in React
What really appeals to me about Mobx is the flexibility (multiple instances rather than centralized) compared to the simplicity (less boilerplate code) of other state management (redux).
Beta Was this translation helpful? Give feedback.
All reactions