地图的MobX地图
MobX map of map
如何制作 observable.map()
的 map/transform?
例如,如果我有一个 observable.map()
的 Todos
,他们的 id
是关键:
var Todos = observable.map({
'rf8r4': {id: 'rf8r4', description: 'Get milk'},
'543w4': {id: '543w4', description: 'Code in MobX'},
'099i0': {id: '099i0', description: 'Sleep'}
})
如何自动生成 UI 状态 observable.map()
,例如包含 visible
属性 默认为 true
(注意键对应上面 Todos
的那些):
ui_state = observable.map({
'rf8r4': {visible: true}, // defaults
'543w4': {visible: false}, // changed to false after some user interaction
'099i0': {visible: true}
})
但要确保每个 Todo 的状态在更改原始 Todos
地图后保持一致,例如:
Todos.set('grtg6', {id: 'grtg6', description: 'Read Whosebug'})
应该导致(注意 543w4
的 visible
属性 仍然是 false
):
ui_state = observable.map({
'rf8r4': {visible: true},
'543w4': {visible: false},
'099i0': {visible: true},
'grtg6': {visible: true} // this is the new one
})
我尝试了 autorun
/createTransformer
,但它最后更新了,就好像这是一个副作用,所以我不能在 React 组件中使用 ui_state
。
我也尝试了不同版本的 computed
,但它每次都会创建一个新的 ui_state
,因为如果我在 computed
函数中使用 createTransformer
,我会得到一个错误...
谢谢!
当您在您所在的州添加新条目时,可能很难将条目添加到您的 ui 地图,但如果您将不在 ui 地图中的条目视为可见,您可以这样做 like this:
const state = observable.map({
'rf8r4': {id: 'rf8r4', description: 'Get milk'},
'543w4': {id: '543w4', description: 'Code in MobX'},
'099i0': {id: '099i0', description: 'Sleep'}
});
@observer
class App extends React.Component {
@observable visible = asMap({
'rf8r4': {visible: false}
});
@computed get visibleState() {
const res = [];
state.forEach(val => {
const item = this.visible.get(val.id);
if (!item || item.visible) {
res.push(val);
}
});
return res;
}
render() {
return <div>
{this.visibleState.map(el =>
<div key={el.id} onClick={this.onClick}> {el.description} </div>
)}
</div>;
}
onClick = () => {
this.visible.set('rf8r4', {visible: true});
};
}
实际上我找到了制作 map
的 constantly-updated 地图的最佳方法。参见MobX Documentation on createTransformer。
我在问题中提到我尝试 createTransformer
并且 运行 遇到了问题。这是因为 autorun
用于 运行ning side-effects,因此在所有 MobX 数据更新后 运行s。这意味着如果某些 MobX 数据(比如 'X')同时依赖于 map
和 map
的映射,那么当 map
被更新时,那么 'X' 已更新,但 X 的另一个依赖项,地图的地图,是陈旧的,因为 autorun
还没有 运行,并且 'X' 的结果值要么是错误或计算本身会产生错误。一旦 autorun
终于 运行 更新了 map
的地图,那么 'X' 必须再次更新。
解决方法是让'X'只依赖map
的map,简单的在原[=]中存储一个引用entry 15=] 在这张 map
的地图中。
index.js:
import Inferno from 'inferno'
import { Provider } from 'inferno-mobx'
import h from 'inferno-hyperscript';
// Components:
import App from './inferno_components/App.js';
// Stores:
import languages from './stores/LanguagesStore.js';
import ui_state from './stores/LanguageViewState.js';
Inferno.render(
h(Provider, {languages, ui_state}, // the following argument can't be an Array:
h(App)
)
, document.getElementById('main'));
// Make random changes post-render to make sure it re-renders:
languages.set('french', {id: 'french', example: 'Bonjour tout le monde'}); // map already passes the second arg through observable, so we don't need to do it explicitly
languages.get('french').example = 'Merci boucoup';
languages.set('german', {id: 'german', example: 'Gutten tag'});
languages.set('swahili', {id: 'swahili', example: 'Rafiki'});
languages.delete('german');
ui_state.get('english').visible = false;
components/App.js:
// for any component:
import { connect } from 'inferno-mobx'
import h from 'inferno-hyperscript';
// components to be used in this component:
import LanguageView from './LanguageView.js';
// the App:
var App = connect(['ui_state'], function({ui_state}){
return h('div', ui_state.keys().map((language_name) => {
var state = ui_state.get(language_name);
if(state.visible)
return h(LanguageView, {language: state.language});
else
return h('p', 'Sorry, invisible!');
}))
});
export default App;
components/LanguageView.js:
import { connect } from 'inferno-mobx'
import h from 'inferno-hyperscript';
var LanguageView = connect(function({language}){
return h('div', [
h('p', language.id),
h('p', language.example)
]);
});
export default LanguageView;
stores/LanguagesStore.js:
import { observable } from 'mobx'
const languages = observable.map({
english: {
id: 'english',
example: 'Hello World'
}
});
export default languages;
stores/LanguageViewState.js:
import { observable, createTransformer, intercept } from 'mobx';
import languages from './LanguagesStore.js';
const makeUIStateFromLanguage = createTransformer((language) => {
return {language, visible: true}
});
var ui_state = observable.map({});
// initialize:
languages.forEach((language, language_name) => {
ui_state.set(language_name, makeUIStateFromLanguage(language));
});
// set up automatic updating:
intercept(languages, function(change_object){
if(change_object.type === 'add'){
ui_state.set(change_object.name, makeUIStateFromLanguage(change_object.newValue));
}
else if(change_object.type === 'delete'){
ui_state.delete(change_object.name);
}
return change_object; // for intercept
});
export default ui_state;
如何制作 observable.map()
的 map/transform?
例如,如果我有一个 observable.map()
的 Todos
,他们的 id
是关键:
var Todos = observable.map({
'rf8r4': {id: 'rf8r4', description: 'Get milk'},
'543w4': {id: '543w4', description: 'Code in MobX'},
'099i0': {id: '099i0', description: 'Sleep'}
})
如何自动生成 UI 状态 observable.map()
,例如包含 visible
属性 默认为 true
(注意键对应上面 Todos
的那些):
ui_state = observable.map({
'rf8r4': {visible: true}, // defaults
'543w4': {visible: false}, // changed to false after some user interaction
'099i0': {visible: true}
})
但要确保每个 Todo 的状态在更改原始 Todos
地图后保持一致,例如:
Todos.set('grtg6', {id: 'grtg6', description: 'Read Whosebug'})
应该导致(注意 543w4
的 visible
属性 仍然是 false
):
ui_state = observable.map({
'rf8r4': {visible: true},
'543w4': {visible: false},
'099i0': {visible: true},
'grtg6': {visible: true} // this is the new one
})
我尝试了 autorun
/createTransformer
,但它最后更新了,就好像这是一个副作用,所以我不能在 React 组件中使用 ui_state
。
我也尝试了不同版本的 computed
,但它每次都会创建一个新的 ui_state
,因为如果我在 computed
函数中使用 createTransformer
,我会得到一个错误...
谢谢!
当您在您所在的州添加新条目时,可能很难将条目添加到您的 ui 地图,但如果您将不在 ui 地图中的条目视为可见,您可以这样做 like this:
const state = observable.map({
'rf8r4': {id: 'rf8r4', description: 'Get milk'},
'543w4': {id: '543w4', description: 'Code in MobX'},
'099i0': {id: '099i0', description: 'Sleep'}
});
@observer
class App extends React.Component {
@observable visible = asMap({
'rf8r4': {visible: false}
});
@computed get visibleState() {
const res = [];
state.forEach(val => {
const item = this.visible.get(val.id);
if (!item || item.visible) {
res.push(val);
}
});
return res;
}
render() {
return <div>
{this.visibleState.map(el =>
<div key={el.id} onClick={this.onClick}> {el.description} </div>
)}
</div>;
}
onClick = () => {
this.visible.set('rf8r4', {visible: true});
};
}
实际上我找到了制作 map
的 constantly-updated 地图的最佳方法。参见MobX Documentation on createTransformer。
我在问题中提到我尝试 createTransformer
并且 运行 遇到了问题。这是因为 autorun
用于 运行ning side-effects,因此在所有 MobX 数据更新后 运行s。这意味着如果某些 MobX 数据(比如 'X')同时依赖于 map
和 map
的映射,那么当 map
被更新时,那么 'X' 已更新,但 X 的另一个依赖项,地图的地图,是陈旧的,因为 autorun
还没有 运行,并且 'X' 的结果值要么是错误或计算本身会产生错误。一旦 autorun
终于 运行 更新了 map
的地图,那么 'X' 必须再次更新。
解决方法是让'X'只依赖map
的map,简单的在原[=]中存储一个引用entry 15=] 在这张 map
的地图中。
index.js:
import Inferno from 'inferno'
import { Provider } from 'inferno-mobx'
import h from 'inferno-hyperscript';
// Components:
import App from './inferno_components/App.js';
// Stores:
import languages from './stores/LanguagesStore.js';
import ui_state from './stores/LanguageViewState.js';
Inferno.render(
h(Provider, {languages, ui_state}, // the following argument can't be an Array:
h(App)
)
, document.getElementById('main'));
// Make random changes post-render to make sure it re-renders:
languages.set('french', {id: 'french', example: 'Bonjour tout le monde'}); // map already passes the second arg through observable, so we don't need to do it explicitly
languages.get('french').example = 'Merci boucoup';
languages.set('german', {id: 'german', example: 'Gutten tag'});
languages.set('swahili', {id: 'swahili', example: 'Rafiki'});
languages.delete('german');
ui_state.get('english').visible = false;
components/App.js:
// for any component:
import { connect } from 'inferno-mobx'
import h from 'inferno-hyperscript';
// components to be used in this component:
import LanguageView from './LanguageView.js';
// the App:
var App = connect(['ui_state'], function({ui_state}){
return h('div', ui_state.keys().map((language_name) => {
var state = ui_state.get(language_name);
if(state.visible)
return h(LanguageView, {language: state.language});
else
return h('p', 'Sorry, invisible!');
}))
});
export default App;
components/LanguageView.js:
import { connect } from 'inferno-mobx'
import h from 'inferno-hyperscript';
var LanguageView = connect(function({language}){
return h('div', [
h('p', language.id),
h('p', language.example)
]);
});
export default LanguageView;
stores/LanguagesStore.js:
import { observable } from 'mobx'
const languages = observable.map({
english: {
id: 'english',
example: 'Hello World'
}
});
export default languages;
stores/LanguageViewState.js:
import { observable, createTransformer, intercept } from 'mobx';
import languages from './LanguagesStore.js';
const makeUIStateFromLanguage = createTransformer((language) => {
return {language, visible: true}
});
var ui_state = observable.map({});
// initialize:
languages.forEach((language, language_name) => {
ui_state.set(language_name, makeUIStateFromLanguage(language));
});
// set up automatic updating:
intercept(languages, function(change_object){
if(change_object.type === 'add'){
ui_state.set(change_object.name, makeUIStateFromLanguage(change_object.newValue));
}
else if(change_object.type === 'delete'){
ui_state.delete(change_object.name);
}
return change_object; // for intercept
});
export default ui_state;