6.1.8 mobx 与react-mobx

625 阅读2分钟

1. mobx

  • observable 用来声明可观察的数据
  • reaction 对 observables变化 作出响应
  • autorun 在执行函数使用的数据变化执行
  • action 动作是任何用来修改状态的东西
const todos = observable([
    {
        title: "Make coffee",
        done: true,
    },
    {
        title: "Find biscuit",
        done: false
    }
]);

const reaction = reaction(
    () => todos.map(todo => todo.title),
    titles => console.log("reaction :", titles.join(", "))
);

const autorun1 = autorun(
    () => console.log("autorun:", todos.map(todo => todo.title).join(","))
);

todos.push({ title: "explain reactions", done: false });
// 输出:
// reaction: Make coffee, find biscuit, explain reactions
// autorun: Make coffee, find biscuit, explain reactions

2. react-mobx

  • Provider以组件的形式存在,用来包裹最外层组件节点,并且传入 store通过context传递给后代组件。
  • observer装饰的react组件将转换成一个监听者,当observable修饰的数据变化,react组件就会重新渲染。
  • inject为了使被装饰的组件以props的形式获取到Provider传递过来的数据。

创建store

import { observable, action } from 'mobx'; // 引入mobx 提供的方法
class baseStore {
  @observable name = "";  
  @action changeName = (name)=>{
      this.name = name;
  }
}
export default new baseStore ();

注入store

import ReactDOM from 'react-dom';
import {Provider} from 'mobx-react';
const stores = {
    baseStore,
};
ReactDOM.render(
    <Provider {...stores}>
        <Page /> 
    </Provider>
 ,document.getElementById('root')
);

使用store

import { observer, inject } from 'mobx-react';
@inject('baseStore')
@observer  
export default class Home extends Component {
    constructor(props) {
        super(props);
        this.state = { };
    }
    componentDidMount() {
      console.log(this.props.baseStore)
    }
    changeName(){
      this.props.baseStore.changeName("hello");
    }
    render(){
      var { name } = this.props.baseStore;
      return (
        <>
          <div>name:{name}</div>
          <button onClick={()=>{this.changeName()}}>更改name</button>
        </>
      )
    }
}

3 响应式原理

mobx整体是一个观察者模式的架构,存储 state 的 store 是被观察者,使用 store 的组件是观察者。
Provider 通过context传递store;
inject 注入依赖的store;
observer(autorun.run)包裹render函数,依赖数据发生变化触发dom更新;

4 mobx-react 源码

  • observer
    observer 函数/装饰器可以用来将 React 组件转换成响应式组件。
    它用 mobx.autorun 包装了组件的 render 函数以确保任何组件在渲染中使用的数据变化时都可以强制刷新组件。 observer 是由单独的 mobx-react 包提供的

  • Provider

var MobXProviderContext =
/*#__PURE__*/
React__default.createContext({});
function Provider(props) {
  var children = props.children,
      stores = _objectWithoutPropertiesLoose(props, ["children"]); 

  var parentValue = React__default.useContext(MobXProviderContext);
  var mutableProviderRef = React__default.useRef(_extends({}, parentValue, {}, stores));
  var value = mutableProviderRef.current;

  if (process.env.NODE_ENV !== "production") {
    var newValue = _extends({}, value, {}, stores); 
  }

  return React__default.createElement(MobXProviderContext.Provider, {
    value: value
  }, children);
}
Provider.displayName = "MobXProvider";
  • inject

核心代码createStoreInjector

function inject() {
  for (var _len = arguments.length, storeNames = new Array(_len), _key = 0; _key < _len; _key++) {
    storeNames[_key] = arguments[_key];
  }

  if (typeof arguments[0] === "function") {
    var grabStoresFn = arguments[0];
    return function (componentClass) {
      return createStoreInjector(grabStoresFn, componentClass, grabStoresFn.name, true);
    };
  } else {
    return function (componentClass) {
      return createStoreInjector(grabStoresByName(storeNames), componentClass, storeNames.join("-"), false);
    };
  }
}

createStoreInjector函数使用forwardRef钩子返回一个新组件;
并将接受到的ref以及 useContext 获取的store通过 props注入到 @inject修饰的类组件中

function createStoreInjector(grabStoresFn, component, injectNames, makeReactive) {
  // React.forwardRef 用于转发ref,并返回一个新组件
  var Injector = React__default.forwardRef(function (props, ref) {
    var newProps = _extends({}, props);

    var context = React__default.useContext(MobXProviderContext);
    Object.assign(newProps, grabStoresFn(context || {}, newProps) || {});

    if (ref) {
      newProps.ref = ref;
    }

    return React__default.createElement(component, newProps);
  });
  
  if (makeReactive) Injector = observer(Injector);
  Injector["isMobxInjector"] = true; 
  copyStaticProperties(component, Injector);
  Injector["wrappedComponent"] = component;
  Injector.displayName = getInjectName(component, injectNames);
  return Injector;
}

参考 juejin.cn/post/690871… segmentfault.com/a/119000002…

欢迎关注我的前端自检清单,我和你一起成长