我正在创建一个简单的React单页应用,即从Behance API提取信息,并在网站上显示。
我使用MobX存储投资组合项目,以及单个项目中的模块a。
store.js
看起来像:
import { observable } from 'mobx';
class Store {
constructor() {
this.storeState = observable({
portfolioItems: [],
singleItem: {},
});
}
}
export default new Store();
我的Single.js
看起来像这样
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import { action, toJS } from 'mobx';
import { css } from 'emotion';
import { getItem } from '../../services/api';
import { errorHandler } from '../../services/errorHandler';
import { isInObservableObject } from '../../helpers/isObservable';
import store from '../../state/store';
import Image from '../../components/Image/Image';
import Text from '../../components/Text/Text';
const singleContainer = css`
display: block;
`;
const backButton = css`
display: block;
padding: 20px;
position: fixed;
bottom: 50px;
right: 30px;
border: 0;
font-size: 16px;
cursor: pointer;
text-transform: uppercase;
color: #FFFFFF;
background-color: rgba(0, 0, 0, 0.5);
`;
@observer
class Single extends Component {
static moduleHandler(module) {
return (module.sizes) ?
(
<Image
alt="Module image"
key={module.id}
src={module.sizes.original}
/>
) :
(
<Text
key={module.id}
type="tertiary"
>
{module.text_plain}
</Text>
);
}
componentWillMount() {
const {match} = this.props;
const itemId = match.params.id;
if (!isInObservableObject(store.storeState.singleItem, itemId)) {
getItem(itemId).then((result) => {
if (typeof result.errors !== 'undefined') {
store.storeState.singleMessage = errorHandler(result.errors);
return;
}
store.storeState.singleItem[itemId] = result.project.modules;
}).catch((error) => {
const errorMessage = `Error: ${error}`;
store.storeState.singleMessage = errorMessage;
});
}
}
@action.bound handleBack() {
const {history} = this.props;
history.goBack();
}
render() {
const { singleItem } = store.storeState;
const { match } = this.props;
const itemId = match.params.id;
const storeObject = toJS(singleItem);
const iterableItem = storeObject[itemId];
return (
<div className={singleContainer}>
{
iterableItem.map((module) => (
this.constructor.moduleHandler(module)
))
}
<button
className={backButton}
onClick={this.handleBack}
>
Go Back
</button>
</div>
);
}
}
export default Single;
我使用php来绕过CORS的问题,因此我用于获取信息的api.js
如下所示:
const fetchData = async (endpoint, method, headers, body) => {
const options = {};
options.method = method;
options.headers = headers || {};
if (typeof body !== 'undefined') {
options.body = JSON.stringify(body);
}
const url = `http://localhost/server.php?project=${endpoint}`;
return fetch(url, options).then((response) => response.json());
};
export function getItems() {
const headers = {
Accept: 'application/json;charset=utf-8',
'Content-Type': 'application/json;charset=utf-8',
};
return fetchData('', 'GET', headers);
}
export function getItem(id) {
const headers = {
Accept: 'application/json;charset=utf-8',
'Content-Type': 'application/json;charset=utf-8',
};
return fetchData(id, 'GET', headers);
}
我面临的问题是:如何从商店中获取数据?使用项目组合项目很简单,因为它们存储在一个数组中,我可以映射它们。
但问题是在singleItem
对象中,我存储了每个项目的模块,并将其存储为可观察对象{$mobx: ObservableObjectAdministration}
,我可以console.log
我可以看到,对于该项目,我得到了正确的模块
62973499: (17) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
但我无法将这些模块拿出来。我试过
singleItem[itemId]
singleItem.itemId
toJS(singleItem)[itemId] // toJS should convert the observable object to regular one
toJS(singleItem).itemId
没什么用的。
我以这种方式存储它们,因为我有20个左右的项目,因此每个项目都应该是可识别的(它们具有我用作关键字的唯一ID)。
答案 0 :(得分:0)
回答我自己的问题,因为我找到了解决这个问题的方法。
首先我更改了商店 - 我的singleItem
现在是Map
import { observable } from 'mobx';
class Store {
constructor() {
this.storeState = observable({
portfolioItems: [],
singleItem: new Map(),
});
}
}
export default new Store();
接下来,我将componentWillMount
更改为
componentWillMount() {
const {match} = this.props;
const itemId = match.params.id;
if (!isInObservableObject(store.storeState.singleItem, itemId)) {
getItem(itemId).then((result) => {
if (typeof result.errors !== 'undefined') {
store.storeState.singleMessage = errorHandler(result.errors);
return;
}
store.storeState.singleItem.set(itemId, result.project.modules);
}).catch((error) => {
const errorMessage = `Error: ${error}`;
store.storeState.singleMessage = errorMessage;
});
}
}
然后我偶然发现了这个
Why componentWillMount is called after rendering?
我用这个对我有利:
render() {
const { singleItem } = store.storeState;
const { match } = this.props;
const itemId = match.params.id;
return (
<div className={singleContainer}>
{
!singleItem.get(itemId) ? (
<Loader />
) : (
singleItem.get(itemId).map((module) => (
this.constructor.moduleHandler(module)
))
)}
<button
className={backButton}
onClick={this.handleBack}
>
Go Back
</button>
</div>
);
}
因此,如果没有提取任何东西,我正在展示我的装载机,当它完成时,我正在显示该项目。
作品:D