mobx 源码学习三

react-mobx 基础学习:observer 和inject

本文是 [mobx 源码初步认识] 第三篇
本文讲解react-mobx连接mobx的方式
该文章采用 react-mobx 较新版本:[v7.1.0]

技术前提

在阅读之前,希望你对以下技术有所了解,不然可能会影响你对本文的理解

1,ES6 装饰器:decorator

2,react组件和props

3,react-context的使用(可选)

4,react-forwardRef的使用(可选)

准备

一 目录结构

├── src
│   ├── types             // react有关类型包括组件,参数,高级组件等
│   ├── utils             // 工具方法
│   ├── disposeOnUnmount  // 重写关于 object,array 等类型的 api
│   ├── index.ts          // 导出封装的方法
│   ├── inject.ts         // 解析store传入到observer
│   |── observer.tsx      // 创建可观察的react组件
|   |── observerClass.ts  // 可观察react组件创建者
|   |—— Provider          // 一种hook的实现方法
└── package.json

二 inject

用于根据storename获取对应store,返回一个方法接收react组件,并且将获取到的store合并到组件的props上

1,inject Api,返回值是一个封装的闭包方法

inject 包括两种传参方式,如果传入function,后续inject中自动会调用oberser

export function inject(...storeNames: Array) {
    if (typeof arguments[0] === "function") {
        // 用法一  传入function
        let grabStoresFn = arguments[0]
        return (componentClass: React.ComponentClass) =>
            createStoreInjector(grabStoresFn, componentClass, grabStoresFn.name, true)
    } else {
        // 用法二 传入storesName
        return (componentClass: React.ComponentClass) =>
            createStoreInjector(
                grabStoresByName(storeNames),
                componentClass,
                storeNames.join("-"),
                false
            )
    }
}

2,grabStoresByName 检查并获取全局通过Provider单向数据流传入的store

function grabStoresByName(
    storeNames: Array
): (baseStores: IValueMap, nextProps: React.Props) => React.PropsWithRef | undefined {
    return function (baseStores, nextProps) {
        storeNames.forEach(function (storeName) {
            if (
                storeName in nextProps // prefer props over stores
            )
                return
            if (!(storeName in baseStores))
                throw new Error(
                    "MobX injector: Store '" +
                    storeName +
                    "' is not available! Make sure it is provided by some Provider"
                )
            nextProps[storeName] = baseStores[storeName]
        })
        return nextProps
    }
}

3,createStoreInjector 合并获取到的指定的Store绑定到该组件的props上,返回一个具有store的props组件对象

function createStoreInjector(
    grabStoresFn: IStoresToProps,
    component: IReactComponent,
    injectNames: string,
    makeReactive: boolean
): IReactComponent {
    // Support forward refs
    let Injector: IReactComponent = React.forwardRef((props, ref) => {
        const newProps = { ...props }
        const context = React.useContext(MobXProviderContext)
        Object.assign(newProps, grabStoresFn(context || {}, newProps) || {})

        if (ref) {
            newProps.ref = ref
        }
        return React.createElement(component, newProps)
    })
    if (makeReactive) Injector = observer(Injector)
    Injector["isMobxInjector"] = true // assigned late to suppress observer warning
    // Static fields from component should be visible on the generated Injector
    copyStaticProperties(component, Injector)
    Injector["wrappedComponent"] = component
    Injector.displayName = getInjectName(component, injectNames)
    return Injector
}

三 observer

1,observer Api

传入带有injectot挂载过store的组件,然后使用组件Observer 组件包裹,Observer组件中调用useObserver

所以最终返回的结果是useObserver包裹的结果

export function observer(component: T): T {
    if (component["isMobxInjector"] === true) {
        console.warn(
            "Mobx observer: You are trying to use 'observer' on a component that already has 'inject'. Please apply 'observer' before applying 'inject'"
        )
    }
    if (ReactForwardRefSymbol && component["$$typeof"] === ReactForwardRefSymbol) {
        const baseRender = component["render"]
        if (typeof baseRender !== "function")
            throw new Error("render property of ForwardRef was not a function")
        return React.forwardRef(function ObserverForwardRef() {
            const args = arguments
            return {() => baseRender.apply(undefined, args)}
        }) as T
    }

    // Function component
    if (
        typeof component === "function" &&
        (!component.prototype || !component.prototype.render) &&
        !component["isReactClass"] &&
        !Object.prototype.isPrototypeOf.call(React.Component, component)
    ) {
        return observerLite(component as React.StatelessComponent) as T
    }

    return makeClassComponentObserver(component as React.ComponentClass) as T
}

2, 组件使用的是mobx-react-lite包中的 ObserverComponent,在ObserverComponent中解析出children,调用useObserver,所以我们应该重点关注useObserver

注意:children 是什么呢?const baseRender = component["render"]

组件中的render方法,render必须是function;否则直接返回null

因此useObserver传入的是组件的render方法,这react-mobx连接mobx和react组件的关键所在

ObserverComponent

function ObserverComponent({ children, render }: IObserverProps) {
    const component = children || render
    if (typeof component !== "function") {
        return null
    }
    return useObserver(component)
}

useObserver

export function useObserver(fn: () => T, baseComponentName: string = "observed"): T {
    if (isUsingStaticRendering()) {
        return fn()
    }
    const [objectRetainedByReact] = React.useState(new ObjectToBeRetainedByReact())
    const forceUpdate = useForceUpdate()
    const reactionTrackingRef = React.useRef(null)

    if (!reactionTrackingRef.current) {
        const newReaction = new Reaction(observerComponentNameFor(baseComponentName), () => {
            if (trackingData.mounted) {
                forceUpdate()
            } else {
                trackingData.changedBeforeMount = true
            }
        })

        const trackingData = addReactionToTrack(
            reactionTrackingRef,
            newReaction,
            objectRetainedByReact
        )
    }

    const { reaction } = reactionTrackingRef.current!
    React.useDebugValue(reaction, printDebugValue)

    React.useEffect(() => {
        recordReactionAsCommitted(reactionTrackingRef)
        if (reactionTrackingRef.current) {
            reactionTrackingRef.current.mounted = true
            if (reactionTrackingRef.current.changedBeforeMount) {
                reactionTrackingRef.current.changedBeforeMount = false
                forceUpdate()
            }
        } else {
            reactionTrackingRef.current = {
                reaction: new Reaction(observerComponentNameFor(baseComponentName), () => {
                    forceUpdate()
                }),
                mounted: true,
                changedBeforeMount: false,
                cleanAt: Infinity
            }
            forceUpdate()
        }

        return () => {
            reactionTrackingRef.current!.reaction.dispose()
            reactionTrackingRef.current = null
        }
    }, [])
    let rendering!: T
    let exception
    reaction.track(() => {
        try {
            rendering = fn()
        } catch (e) {
            exception = e
        }
    })

    if (exception) {
        throw exception 
    }

    return rendering
}

3 ,Reaction 对象

export class Reaction implements IDerivation, IReactionPublic {
    observing_: IObservable[] = [] 
    newObserving_: IObservable[] = []
    dependenciesState_ = IDerivationState_.NOT_TRACKING_
    diffValue_ = 0
    runId_ = 0
    unboundDepsCount_ = 0
    isDisposed_ = false
    isScheduled_ = false
    isTrackPending_ = false
    isRunning_ = false
    isTracing_: TraceMode = TraceMode.NONE

    constructor(
        public name_: string = __DEV__ ? "Reaction@" + getNextId() : "Reaction",
        private onInvalidate_: () => void,
        private errorHandler_?: (error: any, derivation: IDerivation) => void,
        public requiresObservable_ = false
    ) { }

    onBecomeStale_() {
        this.schedule_()
    }
    schedule_() {
        if (!this.isScheduled_) {
            this.isScheduled_ = true
            globalState.pendingReactions.push(this)
            runReactions()
        }
    }
    runReaction_() {
        /*xxxxx*/
    }
    track(fn: () => void) {
        /*xxxxx*/
        const prevReaction = globalState.trackingContext 
        globalState.trackingContext = this
        const result = trackDerivedFunction(this, fn, undefined)
        globalState.trackingContext = prevReaction
    }

    reportExceptionInDerivation_(error: any) {
    }
    dispose() {
        if (!this.isDisposed_) {
            this.isDisposed_ = true
            if (!this.isRunning_) {
                startBatch()
                clearObserving(this)
                endBatch()
            }
        }
    }
    getDisposer_(): IReactionDisposer {
        const r = this.dispose.bind(this) as IReactionDisposer
        r[$mobx] = this
        return r
    }
    toString() {
        return `Reaction[${this.name_}]`
    }
    trace(enterBreakPoint: boolean = false) {
        trace(this, enterBreakPoint)
    }
}

4, trackDerivedFunction

export function trackDerivedFunction(derivation: IDerivation, f: () => T, context: any) {
    const prevAllowStateReads = allowStateReadsStart(true)
    changeDependenciesStateTo0(derivation)
    derivation.newObserving_ = new Array(derivation.observing_.length + 100)
    derivation.unboundDepsCount_ = 0
    derivation.runId_ = ++globalState.runId
    const prevTracking = globalState.trackingDerivation
    globalState.trackingDerivation = derivation
    globalState.inBatch++
    let result
    if (globalState.disableErrorBoundaries === true) {
        result = f.call(context)
    } else {
        try {
            result = f.call(context)
        } catch (e) {
            result = new CaughtException(e)
        }
    }
    globalState.inBatch--
    globalState.trackingDerivation = prevTracking
    bindDependencies(derivation)

    warnAboutDerivationWithoutDependencies(derivation)
    allowStateReadsEnd(prevAllowStateReads)
    return result
}

你可能感兴趣的:(mobx 源码学习三)