(नोट: मैंने JSX हार्मनी विकल्प का उपयोग करके ES6 सिंटैक्स का उपयोग किया है।)
एक अभ्यास के रूप में, मैंने एक नमूना फ्लक्स ऐप लिखा है जो ब्राउज़ करने Github users
और रिपोज करने की अनुमति देता है ।
यह फिशरदेबदेव के उत्तर पर आधारित है, लेकिन एपीआई प्रतिक्रियाओं को सामान्य करने के लिए मेरे द्वारा उपयोग किए जाने वाले दृष्टिकोण को भी दर्शाता है।
मैंने फ्लक्स सीखते हुए कुछ दृष्टिकोणों को दस्तावेज करने के लिए इसे बनाया है।
मैंने इसे वास्तविक दुनिया के करीब रखने की कोशिश की (पेजिनेशन, कोई नकली लोकलस्टोरी एपीआई नहीं)।
यहाँ कुछ बिट्स हैं जिन्हें मैं विशेष रूप से दिलचस्पी लेता था:
कैसे मैं स्टोर वर्गीकृत
मैंने कुछ प्रवाह से बचने की कोशिश की है जो मैंने अन्य फ्लक्स उदाहरण में देखा है, विशेष रूप से स्टोर्स में। मुझे स्टोर को तार्किक रूप से तीन श्रेणियों में विभाजित करना उपयोगी लगा:
कंटेंट स्टोर्स सभी ऐप एंटिटीज़ को होल्ड करते हैं। जिस चीज के लिए ID होती है, उसे अपने Content Store की आवश्यकता होती है। व्यक्तिगत आइटम रेंडर करने वाले घटक ताज़ा डेटा के लिए सामग्री स्टोर से पूछते हैं।
सामग्री स्टोर सभी सर्वर क्रियाओं से अपनी वस्तुओं की कटाई करते हैं । उदाहरण के लिए, यह UserStore
देखता हैaction.response.entities.users
कि क्या मौजूद है, भले ही कार्रवाई निकाल दी गई हो। इसके लिए कोई जरूरत नहीं है switch
। Normalizr इस प्रारूप में किसी भी एपीआई रिपॉन्स को समतल करना आसान बनाता है।
// Content Stores keep their data like this
{
7: {
id: 7,
name: 'Dan'
},
...
}
सूची भंडार कुछ वैश्विक सूची (जैसे "फ़ीड", "आपकी सूचनाएँ") में दिखाई देने वाली संस्थाओं की आईडी पर नज़र रखते हैं। इस परियोजना में, मेरे पास ऐसे स्टोर नहीं हैं, लेकिन मैंने सोचा था कि मैं उनका उल्लेख करूंगा। वे पग-पग संभालते हैं।
वे आम तौर पर बस कुछ ही कार्रवाई का जवाब (जैसे REQUEST_FEED
, REQUEST_FEED_SUCCESS
, REQUEST_FEED_ERROR
)।
// Paginated Stores keep their data like this
[7, 10, 5, ...]
अनुक्रमित सूची भंडार सूची भंडार की तरह हैं लेकिन वे एक-से-कई संबंधों को परिभाषित करते हैं। उदाहरण के लिए, "उपयोगकर्ता के ग्राहक", "रिपोजिटरी के स्टारगेज़र्स", "उपयोगकर्ता के रिपॉजिटरी"। वे भी अंकुरण संभालते हैं।
उन्होंने यह भी सामान्य रूप से बस कुछ ही कार्रवाई का जवाब (जैसे REQUEST_USER_REPOS
, REQUEST_USER_REPOS_SUCCESS
, REQUEST_USER_REPOS_ERROR
)।
अधिकांश सामाजिक ऐप्स में, आपके पास इनमें से बहुत सारे होंगे और आप उनमें से एक को जल्दी से बनाने में सक्षम होना चाहते हैं।
// Indexed Paginated Stores keep their data like this
{
2: [7, 10, 5, ...],
6: [7, 1, 2, ...],
...
}
नोट: ये वास्तविक वर्ग या कुछ नहीं हैं; यह सिर्फ मैं कैसे स्टोर के बारे में सोचना पसंद है। मैंने हालांकि कुछ मदद की।
createStore
यह विधि आपको सबसे मूल स्टोर देती है:
createStore(spec) {
var store = merge(EventEmitter.prototype, merge(spec, {
emitChange() {
this.emit(CHANGE_EVENT);
},
addChangeListener(callback) {
this.on(CHANGE_EVENT, callback);
},
removeChangeListener(callback) {
this.removeListener(CHANGE_EVENT, callback);
}
}));
_.each(store, function (val, key) {
if (_.isFunction(val)) {
store[key] = store[key].bind(store);
}
});
store.setMaxListeners(0);
return store;
}
मैं इसका उपयोग सभी स्टोर बनाने के लिए करता हूं।
isInBag
, mergeIntoBag
सामग्री भंडार के लिए उपयोगी छोटे सहायक।
isInBag(bag, id, fields) {
var item = bag[id];
if (!bag[id]) {
return false;
}
if (fields) {
return fields.every(field => item.hasOwnProperty(field));
} else {
return true;
}
},
mergeIntoBag(bag, entities, transform) {
if (!transform) {
transform = (x) => x;
}
for (var key in entities) {
if (!entities.hasOwnProperty(key)) {
continue;
}
if (!bag.hasOwnProperty(key)) {
bag[key] = transform(entities[key]);
} else if (!shallowEqual(bag[key], entities[key])) {
bag[key] = transform(merge(bag[key], entities[key]));
}
}
}
स्टोर पेजेशन स्टेट और कुछ कथनों को लागू करता है (जब तक, इत्यादि लाने के लिए पेज नहीं लाया जा सकता)।
class PaginatedList {
constructor(ids) {
this._ids = ids || [];
this._pageCount = 0;
this._nextPageUrl = null;
this._isExpectingPage = false;
}
getIds() {
return this._ids;
}
getPageCount() {
return this._pageCount;
}
isExpectingPage() {
return this._isExpectingPage;
}
getNextPageUrl() {
return this._nextPageUrl;
}
isLastPage() {
return this.getNextPageUrl() === null && this.getPageCount() > 0;
}
prepend(id) {
this._ids = _.union([id], this._ids);
}
remove(id) {
this._ids = _.without(this._ids, id);
}
expectPage() {
invariant(!this._isExpectingPage, 'Cannot call expectPage twice without prior cancelPage or receivePage call.');
this._isExpectingPage = true;
}
cancelPage() {
invariant(this._isExpectingPage, 'Cannot call cancelPage without prior expectPage call.');
this._isExpectingPage = false;
}
receivePage(newIds, nextPageUrl) {
invariant(this._isExpectingPage, 'Cannot call receivePage without prior expectPage call.');
if (newIds.length) {
this._ids = _.union(this._ids, newIds);
}
this._isExpectingPage = false;
this._nextPageUrl = nextPageUrl || null;
this._pageCount++;
}
}
createListStore
, createIndexedListStore
,createListActionHandler
बॉयलरप्लेट विधियों और कार्रवाई से निपटने के द्वारा संभव के रूप में सरल के रूप में अनुक्रमित सूची भंडार का निर्माण करता है:
var PROXIED_PAGINATED_LIST_METHODS = [
'getIds', 'getPageCount', 'getNextPageUrl',
'isExpectingPage', 'isLastPage'
];
function createListStoreSpec({ getList, callListMethod }) {
var spec = {
getList: getList
};
PROXIED_PAGINATED_LIST_METHODS.forEach(method => {
spec[method] = function (...args) {
return callListMethod(method, args);
};
});
return spec;
}
/**
* Creates a simple paginated store that represents a global list (e.g. feed).
*/
function createListStore(spec) {
var list = new PaginatedList();
function getList() {
return list;
}
function callListMethod(method, args) {
return list[method].call(list, args);
}
return createStore(
merge(spec, createListStoreSpec({
getList: getList,
callListMethod: callListMethod
}))
);
}
/**
* Creates an indexed paginated store that represents a one-many relationship
* (e.g. user's posts). Expects foreign key ID to be passed as first parameter
* to store methods.
*/
function createIndexedListStore(spec) {
var lists = {};
function getList(id) {
if (!lists[id]) {
lists[id] = new PaginatedList();
}
return lists[id];
}
function callListMethod(method, args) {
var id = args.shift();
if (typeof id === 'undefined') {
throw new Error('Indexed pagination store methods expect ID as first parameter.');
}
var list = getList(id);
return list[method].call(list, args);
}
return createStore(
merge(spec, createListStoreSpec({
getList: getList,
callListMethod: callListMethod
}))
);
}
/**
* Creates a handler that responds to list store pagination actions.
*/
function createListActionHandler(actions) {
var {
request: requestAction,
error: errorAction,
success: successAction,
preload: preloadAction
} = actions;
invariant(requestAction, 'Pass a valid request action.');
invariant(errorAction, 'Pass a valid error action.');
invariant(successAction, 'Pass a valid success action.');
return function (action, list, emitChange) {
switch (action.type) {
case requestAction:
list.expectPage();
emitChange();
break;
case errorAction:
list.cancelPage();
emitChange();
break;
case successAction:
list.receivePage(
action.response.result,
action.response.nextPageUrl
);
emitChange();
break;
}
};
}
var PaginatedStoreUtils = {
createListStore: createListStore,
createIndexedListStore: createIndexedListStore,
createListActionHandler: createListActionHandler
};
एक मिश्रण जो घटकों को स्टोर करने के लिए ट्यून करने की अनुमति देता है, जिसमें वे रुचि रखते हैं, जैसे mixins: [createStoreMixin(UserStore)]
।
function createStoreMixin(...stores) {
var StoreMixin = {
getInitialState() {
return this.getStateFromStores(this.props);
},
componentDidMount() {
stores.forEach(store =>
store.addChangeListener(this.handleStoresChanged)
);
this.setState(this.getStateFromStores(this.props));
},
componentWillUnmount() {
stores.forEach(store =>
store.removeChangeListener(this.handleStoresChanged)
);
},
handleStoresChanged() {
if (this.isMounted()) {
this.setState(this.getStateFromStores(this.props));
}
}
};
return StoreMixin;
}
UserListStore
, जिसमें सभी प्रासंगिक उपयोगकर्ता होंगे। और प्रत्येक उपयोगकर्ता के पास वर्तमान उपयोगकर्ता प्रोफ़ाइल के संबंध का वर्णन करने वाले बूलियन झंडे के एक जोड़े होंगे। जैसे कुछ{ follower: true, followed: false }
, उदाहरण के लिए। तरीकेgetFolloweds()
औरgetFollowers()
यूआई के लिए आवश्यक उपयोगकर्ताओं के विभिन्न सेटों को पुनः प्राप्त करेंगे।