视图层重构

视图层的重构工作主要是使用 observer/@observer 将组件转换成一个个 reaction,同时使用 mobx-react 提供的 inject/@inject 注入组件所需的 store。

首先,在组件树的最外层使用 mobx-react 提供的 Provider 组件注入合并后的 store:

import React from "react";
import ReactDOM from "react-dom";
import { useStrict } from 'mobx';
import { Provider } from "mobx-react";
import App from "./components/App";
import stores from "./stores";

// 在严格模式下,运行MobX
useStrict(true);

ReactDOM.render(
  <Provider {...stores}>
    <App />
  </Provider>,
  document.getElementById("root")
);

组件 App 需要使用 appStore,主要代码如下:

@inject("appStore") // @inject 注入使用的 Store: appStore
@observer // @observer 把 App 组件转化成一个 reaction,自动响应 state 的变化
class App extends Component {
    // ...

    render() {
        const {error, isLoading, removeError} = this.props.appStore;
        const errorDialog = error && (
            <ModalDialog onClose={removeError}>{error.message || error}</ModalDialog>
        );

        return (
            <div>
                <Router>
                    <Switch>
                        <Route exact path="/" component={AsyncHome}/>
                        <Route path="/login" component={AsyncLogin}/>
                        <Route path="/posts" component={AsyncHome}/>
                    </Switch>
                </Router>
                {errorDialog}
                {isLoading && <Loading/>}
            </div>
        );
    }
}

export default App;

再来看一下组件 PostList,它需要使用 postsStore、authStore 和 uiStore 三个 store,代码如下:

import React, {Component} from "react";
import {inject, observer} from "mobx-react";
import PostsView from "./PostsView";
import PostEditor from "../Post/PostEditor";
import "./style.css";

@inject("postsStore", "authStore", "uiStore")
@observer
class PostList extends Component {
    componentDidMount() {
        // 获取帖子列表
        this.props.postsStore.fetchPostList();
    }

    // 保存新建的帖子
    handleSave = data => {
        this.props.postsStore
            .createPost(data)
            .then(() => this.props.uiStore.setAddDialogStatus(false));
    };

    // 取消新建帖子的状态
    handleCancel = () => {
        this.props.uiStore.setAddDialogStatus(false);
    };

    // 设置新建帖子的状态
    handleNewPost = () => {
        this.props.uiStore.setAddDialogStatus(true);
    };

    render() {
        const {postsStore, authStore, uiStore} = this.props;
        return (
            <div className="postList">
                <div>
                    <h2>帖子列表</h2>
                    {authStore.userId ? (
                        <button onClick={this.handleNewPost}>发帖</button>
                    ) : null}
                </div>
                {uiStore.addDialogOpen ? (
                    <PostEditor onSave={this.handleSave} onCancel={this.handleCancel}/>
                ) : null}
                <PostsView posts={postsStore.posts}/>
            </div>
        );
    }
}

export default PostList;

其他组件的改造也都类似,为节约篇幅,这里不再一一介绍,请读者参考源代码。