一个适用于项目中的 mobx 解决方案:react-mobxer
在项目中简单的使用 mobx 解决问题的方案
项目主页: https://github.com/afeiship/react-mobxer
快用
最简单的在项目中使用此工具套件。
npx @jswork/mobxer-init
Why
- mobx 里面的方法很多,我不知道什么时候应该使用哪一个
- 另外:很多方法有新的,或者其它方案替换,但我需要熟悉
mobx/react-mobx/react-mobx-lite
,新手会不清楚应该安装哪种组合
安装
yarn add @jswork/react-mobxer
yarn add --dev @types/webpack-env
使用
- 在 webpack 的环境下,因为依赖于
require.context
harmony
会自动生成 nx.$root 方便操作 mobx 对象
使用
- ConfigProvider: 配置 mobx
- useGlobal: 共享 store
- useLocal: 局部 store
- obs: observer 的别名,来源 react-mobx
index.tsx
harmony
这个是为 @jswork/next 特别留的接口。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.scss';
import App from '@/app';
import reportWebVitals from './reportWebVitals';
import { HashRouter } from 'react-router-dom';
import { ConfigProvider } from '@jswork/react-mobxer';
import "@jswork/next"
const ctx = require.context('./stores/', true, /\.(ts|js|tsx|jsx)$/);
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<HashRouter>
<ConfigProvider context={ctx} harmony>
<App />
</ConfigProvider>
</HashRouter>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
类型 global.d.ts
由于 store 里的具体内容是在项目中生成的,所以这里需要动态维护一个 GlobalStore 的类型 interface
interface IGlobalStore {
user: import('../stores/user');
auth: import('../stores/auth');
}
interface NxStatic {
$root: IGlobalStore;
}
定义 store
import { makeAutoObservable } from 'mobx';
export default class AuthStore {
static storeKey = 'special';
special = 'special var';
constructor() {
makeAutoObservable(this);
}
}
useGlobal
import { obs, useGlobal } from '@jswork/react-mobxer';
export const GlobalCtxComp = obs(() => {
const { user, auth } = useGlobal<IGlobalStore>();
return (
<div>
{user.username} + token: {auth.token}
</div>
);
});
export const AricComp = obs(() => {
const { user, auth } = nx.$root;
return <div>{user.username + ':' + auth.token} + aric.style</div>;
});
useLocal
- 适用于管理多个
state
- 也是
useReducer
的场景
import { useEffect } from 'react';
import { obs, useLocal } from '@jswork/react-mobxer';
export const ProfileMobx = obs(() => {
const store = useLocal({
loading: false,
data: { login: '' },
});
useEffect(() => {
store.loading = true;
fetch('https://api.github.com/users/afeiship')
.finally(() => (store.loading = false))
.then((r) => r.json())
.then((res) => {
store.data = res;
});
}, []);
if (store.loading) return <div>loading...</div>;
return <div>username: {store.data.login}</div>;
});
watch
- 对于一些复杂的对象,我们期望检测到每一个key的变化,然后处理一些逻辑
- 用法:
watch(store, fn, options);
- 这个是我们默认的 options:
const defaults: IAutorunOptions = { delay: 10 };
以下是实现这个 watch 的实现原理: https://playcode.io/1430672
import React, { useEffect } from 'react';
import { observable, reaction, autorun, toJS } from 'mobx';
const store = observable({
filters: {
keywords: 'aaa',
status: 'init',
},
});
function watch(obj, callback) {
return autorun(() => {
callback(toJS(obj));
});
}
// 也可以使用
reaction(
() => {
return JSON.stringify(store);
},
() => {
console.log("obj:", store);
}
);
export const App = () => {
useEffect(() => {
return watch(store.filters, filters => {
console.log('filters changed:', filters);
});
}, []);
return (
<div>
<h1>it works</h1>
<button onClick={()=>{ store.filters.status = Math.random()}}>Change filters1 </button>
<p>
Filters: {store.filters.status1}, {store.filters.status2}
</p>
</div>
);
};
实际项目中的使用: 普通风格
import React, { useEffect } from 'react';
import { watch, obsb } from "@jswork/react-mobxer";
const store = obsb({
filters: {
keywords: 'aaa',
status: 'init',
},
});
export const App = () => {
useEffect(() => {
return watch(store.filters, (plain) => {
console.log("filters changed:", plain);
});
}, []);
return (
<div>
<h1>it works</h1>
<button onClick={()=>{ store.filters.status = Math.random()}}>Change filters1 </button>
<p>
Filters: {store.filters.status1}, {store.filters.status2}
</p>
</div>
);
};
项目使用,useWatch 风格(react hook 风格),适合开发一个小功能,需要用到的
deep-change
的观测。
import React, { useEffect } from 'react';
import { useWatch, obsb } from "@jswork/react-mobxer";
const store = obsb({
filters: {
keywords: 'aaa',
status: 'init',
},
});
export const App = () => {
useWatch(store.filters, (plain) => {
console.log("filters changed:", plain);
});
return (
<div>
<h1>it works</h1>
<button onClick={()=>{ store.filters.status = Math.random()}}>Change filters1 </button>
<p>
Filters: {store.filters.status1}, {store.filters.status2}
</p>
</div>
);
};
脚手架
yo @jswork/react-app:mobxer