5.源码实现 #

5.1 useObserver #

5.1.1 src\Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
import {useObserver} from './mobx-react';
class Store {
    number = 1
    constructor() {
        makeAutoObservable(this, {}, { autoBind: true });
    }
    add() {
        this.number++;
    }
}
let store = new Store();
export default function () {
    return useObserver(()=>(
        <div>
            <p>{store.number}</p>
            <button onClick={store.add}>+</button>
        </div>
    ));
};

5.1.2 src\mobx-react\index.jsx #

src\mobx-react\index.jsx

import React,{useEffect,forwardRef} from 'react';
import { Reaction } from 'mobx';
export function useObserver(fn) {
    const [, setState] = React.useState();
    const forceUpdate = () => setState({});
    const reactionTrackingRef = React.useRef(null);
    if (!reactionTrackingRef.current) {
        const reaction = new Reaction(`observer`, () => {
            forceUpdate();
        });
        reactionTrackingRef.current = { reaction };
    }
    const { reaction } = reactionTrackingRef.current;
    useEffect(() => {
        return () => {
            reactionTrackingRef.current.reaction.dispose();
            reactionTrackingRef.current = null;
        }
    },[]);
    let rendering;
    reaction.track(() => {
        rendering = fn();
    });
    return rendering;
}

5.2 Observer #

5.2.1 src\Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
+import {useObserver,Observer,observer} from './mobx-react';
class Store {
    number = 1
    constructor() {
        makeAutoObservable(this, {}, { autoBind: true });
    }
    add() {
        this.number++;
    }
}
let store = new Store();
export default function () {
    return (
+       <Observer>
            {
                ()=>(
                  <>
                    <p>{store.number}</p>
                    <button onClick={store.add}>+</button>
                  </>
                )
            }
+       </Observer>
    )
}

5.2.2 src\mobx-react\index.jsx #

src\mobx-react\index.jsx

import React,{useEffect,forwardRef} from 'react';
import { Reaction } from 'mobx';
export function useObserver(fn) {
    const [, setState] = React.useState();
    const forceUpdate = () => setState({});
    const reactionTrackingRef = React.useRef(null);
    if (!reactionTrackingRef.current) {
        const reaction = new Reaction(`observer`, () => {
            forceUpdate();
        });
        reactionTrackingRef.current = { reaction };
    }
    const { reaction } = reactionTrackingRef.current;
    useEffect(() => {
        return () => {
            reactionTrackingRef.current.reaction.dispose();
            reactionTrackingRef.current = null;
        }
    },[]);
    let rendering;
    reaction.track(() => {
        rendering = fn();
    });
    return rendering;
}

+export function Observer({ children }) {
+    return useObserver(children);
+}

5.3 observer #

5.3.1 src\Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
import {useObserver,Observer,observer} from './mobx-react';
class Store {
    number = 1
    constructor() {
        makeAutoObservable(this, {}, { autoBind: true });
    }
    add() {
        this.number++;
    }
}
let store = new Store();
+export default observer(function () {
    return (
        <div>
            <p>{store.number}</p>
            <button onClick={store.add}>+</button>
        </div>
    )
}); 

5.3.2 src\mobx-react\index.jsx #

src\mobx-react\index.jsx

import React,{useEffect,forwardRef} from 'react';
import { Reaction } from 'mobx';
export function useObserver(fn) {
    const [, setState] = React.useState();
    const forceUpdate = () => setState({});
    const reactionTrackingRef = React.useRef(null);
    if (!reactionTrackingRef.current) {
        const reaction = new Reaction(`observer`, () => {
            forceUpdate();
        });
        reactionTrackingRef.current = { reaction };
    }
    const { reaction } = reactionTrackingRef.current;
    useEffect(() => {
        return () => {
            reactionTrackingRef.current.reaction.dispose();
            reactionTrackingRef.current = null;
        }
    },[]);
    let rendering;
    reaction.track(() => {
        rendering = fn();
    });
    return rendering;
}

export function Observer({ children }) {
    return useObserver(children);
}
+export function observer(baseComponent) {
+    let observerComponent = (props) => {
+        return useObserver(() => baseComponent(props));
+    };
+    return observerComponent;
+}

5.4 observer #

5.4.1 src\Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
import {useObserver,Observer,observer} from './mobx-react';
class Store {
    number = 1
    constructor() {
        makeAutoObservable(this, {}, { autoBind: true });
    }
    add() {
        this.number++;
    }
}
let store = new Store();
export default observer(function () {
    return (
        <div>
            <p>{store.number}</p>
            <button onClick={store.add}>+</button>
        </div>
    )
},{forwardRef:true}); 

5.4.2 src\mobx-react\index.jsx #

src\mobx-react\index.jsx

import React,{useEffect,forwardRef,memo} from 'react';
import { Reaction } from 'mobx';
export function useObserver(fn) {
    const [, setState] = React.useState();
    const forceUpdate = () => setState({});
    const reactionTrackingRef = React.useRef(null);
    if (!reactionTrackingRef.current) {
        const reaction = new Reaction(`observer`, () => {
            forceUpdate();
        });
        reactionTrackingRef.current = { reaction };
    }
    const { reaction } = reactionTrackingRef.current;
    useEffect(() => {
        return () => {
            reactionTrackingRef.current.reaction.dispose();
            reactionTrackingRef.current = null;
        }
    },[]);
    let rendering;
    reaction.track(() => {
        rendering = fn();
    });
    return rendering;
}

export function Observer({ children }) {
    return useObserver(children);
}

+export function observer(baseComponent, options) {
+    let useForwardRef = options?.forwardRef ?? false;
+    let observerComponent = (props, ref) => {
+        return useObserver(() => baseComponent(props, ref));
+    };
+    if (useForwardRef) {
+        observerComponent = forwardRef(observerComponent);
+    }
+    return memo(observerComponent);
+}

5.5 useLocalObservable #

5.5.1 src\main.jsx #

src\main.jsx

import { createRoot } from "react-dom/client";
import Counter from "./Counter";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<Counter value={5}/>);

5.5.2 src\Counter.jsx #

src\Counter.jsx

import React from 'react';
+import {useObserver,useLocalObservable} from 'mobx-react';
export default function (props) {
+   const store = useLocalObservable(()=>({
+       number:props.value,
+       add(){
+           this.number++;
+       },
+       get double(){
+           return this.number*2;
+       }
+   }));
    return useObserver(()=>(
        <div>
            <p>{store.number}</p>
            <button onClick={store.add}>+</button>
            <p>{store.double}</p>
        </div>
    ));
};

5.5.3 mobx-react\index.jsx #

src\mobx-react\index.jsx

import React,{useEffect,forwardRef,memo,useState} from 'react';
import { Reaction,observable } from 'mobx';
import { runInAction } from 'mobx';
export function useObserver(fn) {
    const [, setState] = React.useState();
    const forceUpdate = () => setState({});
    const reactionTrackingRef = React.useRef(null);
    if (!reactionTrackingRef.current) {
        const reaction = new Reaction(`observer`, () => {
            forceUpdate();
        });
        reactionTrackingRef.current = { reaction };
    }
    const { reaction } = reactionTrackingRef.current;
    useEffect(() => {
        return () => {
            reactionTrackingRef.current.reaction.dispose();
            reactionTrackingRef.current = null;
        }
    },[]);
    let rendering;
    reaction.track(() => {
        rendering = fn();
    });
    return rendering;
}

export function Observer({ children }) {
    return useObserver(children);
}

export function observer(baseComponent, options) {
    let useForwardRef = options?.forwardRef ?? false;
    let observerComponent = (props, ref) => {
        return useObserver(() => baseComponent(props, ref));
    };
    if (useForwardRef) {
        observerComponent = forwardRef(observerComponent);
    }
    return memo(observerComponent);
}
+export function useLocalObservable(initializer){
+    return useState(() => observable(initializer(), {}, { autoBind: true }))[0];
+}

5.6 observer #

5.6.1 src\Counter.jsx #

src\Counter.jsx

import React from 'react';
import {makeAutoObservable} from 'mobx';
import {observer} from './mobx-react';
class Store {
    number = 1
    constructor() {
        makeAutoObservable(this, {}, { autoBind: true });
    }
    add() {
        this.number++;
    }
}
let store = new Store();

@observer
class Counter extends React.Component{
  render(){
    return (
        <div>
            <p>{store.number}</p>
            <button onClick={store.add}>+</button>
        </div>
    )
  }
}
export default  Counter;

5.6.2 src\mobx-react\index.jsx #

src\mobx-react\index.jsx

import React,{useEffect,forwardRef,memo,useState,Component} from 'react';
import { Reaction,observable } from 'mobx';
export function useObserver(fn) {
    const [, setState] = React.useState();
    const forceUpdate = () => setState({});
    const reactionTrackingRef = React.useRef(null);
    if (!reactionTrackingRef.current) {
        const reaction = new Reaction(`observer`, () => {
            forceUpdate();
        });
        reactionTrackingRef.current = { reaction };
    }
    const { reaction } = reactionTrackingRef.current;
    useEffect(() => {
        return () => {
            reactionTrackingRef.current.reaction.dispose();
            reactionTrackingRef.current = null;
        }
    },[]);
    let rendering;
    reaction.track(() => {
        rendering = fn();
    });
    return rendering;
}

export function Observer({ children }) {
    return useObserver(children);
}

export function observer(baseComponent, options) {
+   if(baseComponent.prototype.isReactComponent){
+       return makeClassComponentObserver(baseComponent);
+   }
    let useForwardRef = options?.forwardRef ?? false;
    let observerComponent = (props, ref) => {
        return useObserver(() => baseComponent(props, ref));
    };
    if (useForwardRef) {
        observerComponent = forwardRef(observerComponent);
    }
    return memo(observerComponent);
}

+export function makeClassComponentObserver(componentClass){
+    const target = componentClass.prototype
+    const originalRender = target.render
+    target.render = function () {
+        const boundOriginalRender = originalRender.bind(this)
+        const reaction = new Reaction(`render`, () =>  Component.prototype.forceUpdate.call+(this))
+        let rendering;
+        reaction.track(() => {
+            rendering = boundOriginalRender();
+        })
+        return rendering
+    }
+    return componentClass
+}
export function useLocalObservable(initializer){
    return useState(() => observable(initializer(), {}, { autoBind: true }))[0];
}