Welcome to pay attention to the public number [code attack], more exciting content please pay attention to the latest news of the public number.
The demo address
Q: What exactly does DVA Subscriptions consist of? A: If you need to subscribe to some data and only have logic that deals with current models, then you should use subscriptions.
The description of subscriptions in the official documents was too simple, so that many students were not very clear about the concept.
1. Code analysis
The key code associated with subscription is extracted from the source code as follows:
// index.js
import { run as runSubscription, unlisten as unlistenSubscription } from './subscription';
/** * Create dva-core instance. */
export function create(hooksAndOpts = {}, createOpts = {}) {
/ /...
const app = {
_models: [prefixNamespace({ ...dvaModel })],
_store: null._plugin: plugin,
use: plugin.use.bind(plugin),
model,
start,
};
return app;
/ /...
/** * Register model before app is started. */
function model(m) {
if(process.env.NODE_ENV ! = ='production') {
checkModel(m, app._models);
}
constprefixedModel = prefixNamespace({ ... m }); app._models.push(prefixedModel);return prefixedModel;
}
/** * Inject model after app is started. */
function injectModel(createReducer, onError, unlisteners, m) {
m = model(m);
const store = app._store;
store.asyncReducers[m.namespace] = getReducer(m.reducers, m.state, plugin._handleActions);
store.replaceReducer(createReducer());
if (m.effects) {
store.runSaga(app._getSaga(m.effects, m, onError, plugin.get('onEffect'), hooksAndOpts));
}
if(m.subscriptions) { unlisteners[m.namespace] = runSubscription(m.subscriptions, m, app, onError); }}/** * Unregister model. */
function unmodel(createReducer, reducers, unlisteners, namespace) {
const store = app._store;
// Delete reducers
delete store.asyncReducers[namespace];
delete reducers[namespace];
store.replaceReducer(createReducer());
store.dispatch({ type: '@@dva/UPDATE' });
// Cancel effects
store.dispatch({ type: `${namespace}/@@CANCEL_EFFECTS` });
// Unlisten subscrioptions
unlistenSubscription(unlisteners, namespace);
// Delete model from app._models
app._models = app._models.filter(model= >model.namespace ! == namespace); }/**
* Start the app.
*
* @returns void* /
function start() {
/ /...
// Run subscriptions
const unlisteners = {};
for (const model of this._models) {
if(model.subscriptions) { unlisteners[model.namespace] = runSubscription(model.subscriptions, model, app, onError); }}// Setup app.model and app.unmodel
app.model = injectModel.bind(app, createReducer, onError, unlisteners);
app.unmodel = unmodel.bind(app, createReducer, reducers, unlisteners);
app.replaceModel = replaceModel.bind(app, createReducer, reducers, unlisteners, onError);
/ /...}}Copy the code
// subscription.js
export function run(subs, model, app, onError) {
const funcs = [];
const nonFuncs = [];
for (const key in subs) {
if (Object.prototype.hasOwnProperty.call(subs, key)) {
const sub = subs[key];
const unlistener = sub(
{
dispatch: prefixedDispatch(app._store.dispatch, model),
history: app._history,
},
onError,
);
if (isFunction(unlistener)) {
funcs.push(unlistener);
} else{ nonFuncs.push(key); }}}return { funcs, nonFuncs };
}
Copy the code
All the run method does is traverse the Subscriptions configured in the Model and pass the Dispatch method and history object as parameters to each subscription configured.
We can see from the code that all the Subscriptions to Model.model registered by app.model will be processed through the start method and the returned values will be collected into unlisteners[model.namespace]. Used to unsubscribe data sources when app.unmodel(namespace).
If subscriptions does not return a function, it will be warned when app.unmodel is called.
// Run subscriptions
const unlisteners = {};
for (const model of this._models) {
if(model.subscriptions) { unlisteners[model.namespace] = runSubscription(model.subscriptions, model, app, onError); }}Copy the code
In addition, prefixedDispatch(app._store.dispatch, model) is passed as dispatch to the subscription configured method in the run method of the subscription. The condensed code looks like this:
function prefixedDispatch(dispatch, model){
return action= >{ app._store.dispatch({ ... action,type: `${model.namespace}${NAMESPACE_SEP}${action.type}`}}})Copy the code
Therefore, it can be seen that only reducer and effects in the current model can be dispatched in the SUBSCRIPTIONS.
Conclusion 2.
From the code we can draw the following conclusions:
- The names of keys configured in subscriptions have no constraints and are only useful when app.unmodel.
- Only the reducer and effects of the model can be dispatched in the SUBSCRIPTIONS configuration.
- The subscriptions function is only executed once, traversing all model subscriptions when app.start() is called.
- The functions configured in Subscriptions need to return a function that should be used to unsubscribe the data source.
3. Code examples
// models/Products.js
export default {
namespace: 'products'./ /...
subscriptions: {
setupxxx({ dispatch, history }) {
// history.listen Returns unlisten
return history.listen(({ pathname, query }) = > {
console.log('history')}); },i_am_just_a_name({dispatch}){
console.log('into')
function handleClick() {
console.log('hello')
dispatch({
type: 'delete'.payload: 1})}document.addEventListener('click', handleClick);
// Return a function to remove the click event
return () = > {
document.removeEventListener('click', handleClick)
}
}
},
/ /...
};
Copy the code