![](/img/trans.png)
[英]React/MobX component not re-rendering after state change (found a weird solution, would like to know why)
[英]MobX State Tree async actions and re-rendering React component
我是 MST 的新手,很難找到更多帶有異步操作的示例。 我有一個 api,它將根據您傳遞給它的參數返回不同的數據。 在這種情況下,api 可以返回一組照片或教程。 我已經為商店設置了我的初始值,如下所示:
data: {
photos: [],
tutorials: []
}
目前,我正在使用applySnapshot
來更新商店,最終,這將觸發我的 React 組件的重新渲染。 為了同時顯示照片和教程,我需要調用 api 兩次(一次是照片參數,第二次是教程)。 我遇到了一個問題,第一次更新的快照顯示照片和教程具有相同的值,只有在第二次更新時,我才能獲得正確的值。 我可能誤用了applySnapshot
來重新渲染我的 React 組件。 我想知道這樣做的更好/正確方法。 在 api 產生響應后重新渲染我的 React 組件的最佳方法是什么? 任何建議都非常感謝
我是這樣設置我的商店的:
import { RootModel } from '.';
import { onSnapshot, getSnapshot, applySnapshot } from 'mobx-state-tree';
export const setupRootStore = () => {
const rootTree = RootModel.create({
data: {
photos: [],
tutorials: []
}
});
// on snapshot listener
onSnapshot(rootTree, snapshot => console.log('snapshot: ', snapshot));
return { rootTree };
};
我使用生成器創建了以下帶有異步操作的模型:
import {types,Instance,applySnapshot,flow,onSnapshot} from 'mobx-state-tree';
const TestModel = types
.model('Test', {
photos: types.array(Results),
tutorials: types.array(Results)
})
.actions(self => ({
fetchData: flow(function* fetchData(param) {
const results = yield api.fetch(param);
applySnapshot(self, {
...self,
photos: [... results, ...self.photos],
tutorials: [... results, ...self.tutorials]
});
})
}))
.views(self => ({
getPhoto() {
return self.photos;
},
getTutorials() {
return self.tutorials;
}
}));
const RootModel = types.model('Root', {
data: TestModel
});
export { RootModel };
export type Root = Instance<typeof RootModel>;
export type Test = Instance<typeof TestModel>;
用於Photos.tsx
React 組件
import React, { Component } from 'react';
import Spinner from 'components/Spinner';
import { Root } from '../../stores';
import { observer, inject } from 'mobx-react';
interface Props {
rootTree?: Root
}
@inject('rootTree')
@observer
class Photos extends Component<Props> {
componentDidMount() {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.data.fetchData('photo');
}
componentDidUpdate(prevProps) {
if (prevProps.ctx !== this.props.ctx) {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.data.fetchData('photo');
}
}
displayPhoto() {
const { rootTree } = this.props;
if (!rootTree) return null;
// calling method in MST view
const photoResults = rootTree.data.getPhoto();
if (photoResults.$treenode.snapshot[0]) {
return (
<div>
<div className='photo-title'>{'Photo'}</div>
{photoResults.$treenode.snapshot.map(Item => (
<a href={photoItem.attributes.openUrl} target='_blank'>
<img src={photoItem.url} />
</a>
))}
</div>
);
} else {
return <Spinner />;
}
}
render() {
return <div className='photo-module'>{this.displayPhoto()}</div>;
}
}
export default Photos;
同樣,Tutorials.tsx 是這樣的:
import React, { Component } from 'react';
import Spinner from '';
import { Root } from '../../stores';
import { observer, inject } from 'mobx-react';
interface Props {
rootTree?: Root;
}
@inject('rootTree')
@observer
class Tutorials extends Component<Props> {
componentDidMount() {
if (this.props.ctx) {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.data.fetchData('tuts');
}
}
componentDidUpdate(prevProps) {
if (prevProps.ctx !== this.props.ctx) {
const { rootTree } = this.props;
if (!rootTree) return null;
rootTree.search.fetchData('tuts');
}
}
displayTutorials() {
const { rootTree } = this.props;
if (!rootTree) return null;
// calling method in MST view
const tutResults = rootTree.data.getTutorials();
if (tutResults.$treenode.snapshot[0]) {
return (
<div>
<div className='tutorials-title'>{'Tutorials'}</div>
{tutResults.$treenode.snapshot.map(tutorialItem => (
<a href={tutorialItem.attributes.openUrl} target='_blank'>
<img src={tutorialItem.url} />
</a>
))}
</div>
);
} else {
return <Spinner />;
}
}
render() {
return <div className='tutorials-module'>{this.displayTutorials()}</div>;
}
}
export default Tutorials;
在這種情況下,您為什么要使用applySnapshot
? 我認為沒有必要。 只需在您的操作中根據需要分配您的數據:
.actions(self => ({
//If you're fetching both at the same time
fetchData: flow(function* fetchData(param) {
const results = yield api.fetch(param);
//you need cast() if using Typescript otherwise I think it's optional
self.photos = cast([...results.photos, ...self.photos])
//do you really intend to prepend the results to the existing array or do you want to overwrite it with the sever response?
self.tutorials = cast(results.tutorials)
})
}))
或者,如果您需要發出兩個單獨的請求來獲取數據,最好將其設為兩個不同的操作
.actions(self => ({
fetchPhotos: flow(function* fetchPhotos(param) {
const results = yield api.fetch(param)
self.photos = cast([... results, ...self.photos])
}),
fetchTutorials: flow(function* fetchTutorials(param) {
const results = yield api.fetch(param)
self.tutorials = cast([... results, ...self.tutorials])
}),
}))
無論如何,您似乎不需要applySnapshot
。 只需根據需要在您的操作中分配您的數據。 在異步操作中分配數據沒有什么特別之處。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.