✋手摸手系列(一/3): vite react typescript reactHook mobx(非脚手架)(从0开始):mobx 接入

558 阅读3分钟

继续上一篇:✋手摸手系列(一/2): vite react typescript reactHook mobx(非脚手架)(从0开始):路由和权限搭建

后台系列目录

  1. ✋手摸手系列(一/1): vite react typescript reactHook mobx(非脚手架)(从0开始)(包含路由权限控制)

  2. ✋手摸手系列(一/2): vite react typescript reactHook mobx(非脚手架)(从0开始):路由和权限搭建

  3. ✋手摸手系列(一/3): vite react typescript reactHook mobx(非脚手架)(从0开始):mobx 接入


参考资料:mobx.js.org/README.html

项目地址:github.com/wowhoonet/v…

安装依赖

yarn add mobx mobx-react

创建文件 store->user.ts 代码如下:

import { IUser } from "@/interface/user";
import { observable } from "mobx";

export const user = observable<{
  token: string;
  user: IUser
}>({
  user: {} as IUser,
  token: ''
});

observable是将对象监听,实现proxy代理,如果要将数据双向绑定就需要observable装饰数据,mobx 默认推荐的是 class 装饰器模式@observable写法,但是我们这里用的 hook 所以还是用都用 function写法了

创建 interface-> user.ts 代码如下:

export interface IUser {
  name: string,
  age: number
}

开始实现登录逻辑

创建 service-> login.ts 代码如下:

import { IUser } from "@/interface/user";
import { user } from "@/store/user";
import { runInAction } from "mobx";

export const loginService = {
  logIn({}: { userName: string; password: string }): Promise<IUser> {
    // 模拟请求
    return new Promise((resolve, reject) => {
      const rep =  {
        token: 'xxx',
        user: {
          name: 'wowhoo',
          age: 24
        }
      }
      // 将两次赋值操作放入一次 action 动作内,避免重复通知
      runInAction(() => {
        user.token = rep.token;
        user.user = rep.user;
      })
      resolve(rep.user)
    })
  },
};

需要解释一下,我这里的 service 层是逻辑处理层,主要是通过 api请求改变本地的 store 数据,避免直接在页面引用 store 然后修改,这样无法约定跟踪;

加入登录逻辑

修改 wrapper->auth.tsx

import React from 'react';
import { Redirect } from 'react-router';
import {user} from '@/store/user';

export default function Auth (props: {
  children: React.ReactElement
}): React.ReactElement {
  const isLogin =  !!user.token;
  const {children, ...cprops} = props;
  if(isLogin) {
    return children
  }
  return <Redirect to="/login"></Redirect>;
}

判断用户的 token 存不存在,根据业务逻辑,此处仅供参考具体可以根据业务逻辑

修改 view-> login-> index.tsx

import React from 'react';
import {loginService} from '@/service/login'
import { useHistory } from 'react-router';

export default function Login (): React.ReactElement {
  const history = useHistory();

  const onLogin = () => {
    loginService.logIn({
      userName: 'wowhoo',
      password: '123',
    }).then(() => {
      history.push('/home')
    })
  }
  return <div>
    登录页面
    <button onClick={onLogin}>登录</button>
  </div>;
}

useHistory是 reactRouter 的 hook 写法;这里点击登录的时候调用了 service 层暴露的 logIn 方法

修改 view-> home-> index.tsx

import React from 'react';
import { useObserver } from 'mobx-react';
import {user} from '@/store/user'

export default function Home (): React.ReactElement {


  return useObserver(() => {
    return <div>
      username: {user.user.name}
      年龄:${user.user.age}
    </div>
  });
}

需要注意的是useObserver的用法,useObserver是将包裹的组件动态化,能根据 store 的值来重新 render,这也是 mobx 的一个比较便捷的地方

Run!run 起来

我们先访问http://localhost:3000/#/home 发现会重定向/login,这是我们 auth 拦截器在生效,然后我们点击登录,跳转到/home,这是我们实现了登录逻辑,将 store 里面塞入了 token 信息,auth 拦截器校验通过

总结

  1. 引入 mobx 系列包,包含 mobx 基础库,和 mobx-react 的 react 版本,后面这个主要是使用 mobx 能够动态改变ui
  2. 使用 service 层来改变 store,避免在非 service 层改变 store,原则上应该遵守数据追溯
  3. 使用observable修饰实体,实现监听;如果一个方法中多次改变同一个 store 则需要使用 runInaction 方法包裹;使用useObserver将Ui 根据 store 改变自动刷新;

下一篇结合 antd 和 按需加载,晚安(¦3[▓▓] 晚安