解决控制台 MobX 警告
MobX: Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed
MobX 在启用严格模式时,修改 observable state 的操作必须在 action 中进行。在异步动作的回调中执行的代码是不会被 action 包装的。在回调中修改 observable state 是无法通过 enforceActions 检查的。官方文档的解决办法是使用 Flow。但是因为需要用到 generator 和 yield,我选择使用了更简单的方法。
使用  runInAction 包裹在回调函数中执行的修改操作。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | @action find() {this.isLoading = true;
 if (this.hasMore) {
 Uploader.find(this.page, this.limit).then(
 (newList: any) => {
 this.append(newList);
 runInAction(()=>{
 this.page += 1;
 if (newList.length < this.limit) {
 this.hasMore = false;
 }
 })
 }
 ).catch(() => {
 message.error('加载失败').then();
 })
 }
 }
 
 | 
MobX 6 使用装饰器踩坑
因为装饰器语法目前还不是 ES 的标准,所以 MobX 6  不再推荐使用,而更建议使用 makeObservable / makeAutoObservable 来代替。
如果想要继续使用原来的写法,则需要在构造函数中添加 makeObservable(this)
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | class Todo {id = Math.random()
 @observable title = ""
 @observable finished = false
 
 constructor() {
 
 makeObservable(this)
 }
 
 @action
 toggle() {
 this.finished = !finished
 }
 }
 
 | 
React.lazy 和 suspense
React.lazy 可以实现组件的延迟加载,只有在组件渲染时再去加载包含引入路径的组件。
| 1
 | const Home = lazy(() => import('./pages/Home'))
 | 
而在延迟加载的过程中会有 loading 的过程,suspend 可以控制在 loading 过程中的显示。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 
 | <Suspense fallback={<div>Loading ...</div>}><Switch>
 <Route path="/" exact>
 <Home/>
 </Route>
 <Route path="/history">
 <History/>
 </Route>
 <Route path="/about">
 <About/>
 </Route>
 <Route path="/login">
 <Login/>
 </Route>
 <Route path="/register">
 <Register/>
 </Route>
 </Switch>
 </Suspense>
 
 | 
使用 antd
在使用 mobx 搭配 antd 时发现删除 HistoryStore.list 中的数据并没有触发 UI 更新。在 StackOverflow 我发现了这个回答,解决了我的问题。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 
 | const Table = observer(() => {const {HistoryStore} = useStore();
 const loadMore = () => {
 HistoryStore.find();
 };
 useEffect(() => {
 return () => {
 HistoryStore.reset();
 };
 }, [HistoryStore]);
 return (
 <InfiniteScroll initialLoad={true} pageStart={1} loadMore={loadMore}
 hasMore={!HistoryStore.isLoading && HistoryStore.hasMore} useWindow={true}>
 <ListWrapper dataSource={toJS(HistoryStore.list)} renderItem={(item: any) => {
 return (<List.Item key={item.id}>
 <Img src={item.attributes.image.attributes.url} alt=""/>
 <h5>{item.attributes.filename}</h5>
 <Href href={item.attributes.image.attributes.url} target="_blank"
 rel="noreferrer">{item.attributes.image.attributes.url}</Href>
 <div >
 <Button type="primary" onClick={() => HistoryStore.remove(item.id)}>删除图片</Button>
 </div>
 </List.Item>);
 }}>
 {HistoryStore.isLoading && HistoryStore.hasMore && (
 <Loading>
 <Spin tip="加载中"/>
 </Loading>
 )}
 </ListWrapper>
 </InfiniteScroll>
 );
 });
 
 | 
原因是 mobx 在 Table 组件中只会去监听 HistoryStore.list 指向的变化,并不会监听 HistoryStore.list 具体某一项属性的改变。而使用 toJS 创建克隆对象(读取HistoryStore 的每一项属性)后,mobx 可以在 Table 组件中监听到 list 长度的变化,从而触发 Table 组件重新渲染。事实上在这里改成下面的形式也可以触发重新渲染:
| 1
 | <ListWrapper dataSource={HistoryStore.list.length && History.list}/> 
 | 
这里有更明显的例子。
总结