Using Higher-Order Functions for Login State Management in Mini Programs
Understanding Higher-Order Functions
A higher-order function is a function that either takes another function as an argument or returns a function as its result. This concept isn't as complex as it might sound. Common array methods likee Array.prototype.forEach, Array.prototype.map, and Array.prototype.reduce are all examples of higher-order functions. Techniques like debouncing and throttling also rely on this pattern.
Login Status Verification in Mini Programs
In earlier versions of mini programs, login verification typically occurred immediately upon app entry. If the user wasn't logged in, they would be redirected to a login page. While this approach simplified development by eliminating the need to check login status within individual event handlers, it had significant drawbacks: users couldn't access any features without logging in, even those that shouldn't require authentication (like introduction pages). Additionally, starting September 1st, apps using this method may fail review processes.
To address these issues, we now allow guest access to non-restricted pages and implement login checks specifically where authenticated actions are required—such as button clicks for liking, purchasing, or navigating to protected sections.
This change introduces a new challenge: repetitive code insertion into multiple event handlers:
// Assuming login state is stored in app.globalData (similar principles apply to redux/vuex)
if (!getApp().globalData.isAuthenticated) {
this.redirectToLogin();
return;
}
// Actual business logic follows...
Duplicating this pattern across five or six locations becomes tedious and hard to maintain. To resolve this, we can extract the repeated logic using a higher-order function.
Creating a Reusable Authentication Wrapper
Following our earlier reasoning and definition of higher-order functions, let's create a reusable wrapper that accepts an event handler and returns a new one with built-in authentication checking:
function withAuthCheck(originalHandler, context) {
return function(...params) {
const appInstance = getApp();
if (!appInstance.globalData.isAuthenticated) {
this.navigateToLogin();
return;
}
return originalHandler.apply(context, params);
};
}
We then export this utility from a module:
// authGuard.js
export default function withAuthCheck(originalHandler, context) {
// implementation here
}
When applying authentication checks to specific event handlers, simply wrap them:
import withAuthCheck from './authGuard.js';
this.handleClick = withAuthCheck((event) => {
// Protected action logic
}, this);
Beyond manual wrapping, we can leverage decorators for cleaner syntax (those unfamiliar with decorators should refer to additional resources):
// Event handler with decorator syntax
@withAuthCheck
handleLikeAction() {
// Like functionality requiring auth
}
// Decorator implementation
function withAuthCheck(target, propertyName, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args) {
const appState = getApp();
if (!appState.globalData.isAuthenticated) {
this.showLoginPrompt();
return;
}
return originalMethod.apply(this, args);
};
return descriptor;
}
This approach effectively isolates and reuses authentication logic throughout your mini program.