features-applier
v1.3.3
Published
A React library that streamlines the development of complex React components through modularity, composability and maintainability.
Downloads
11
Maintainers
Readme
A React library that streamlines the development of complex React components through modularity, composability and maintainability.
Table of Contents
Introduction
Features Applier is based on a systematic approach designed to enhance the development of complex React components. It emphasizes modularity, scalability and maintainability, ensuring that components are robust and adaptable.
Key Concepts of Features Applier
- Modular Design - each component is constructed as a self-contained unit with its own set of responsibilities and interfaces. This allows components to be easily managed, tested, and reused across different parts of an application or in different projects.
- Separation of Concerns - distinctly separates the visual representation (UI) from the business logic (UX/logic layer) within components. This separation simplifies management and enhances the clarity of both the component’s design and its operational logic.
- Composability - Leverages High Order Components (HOCs) and React Hooks to enhance and extend the functionalities of components. This approach allows new features to be layered onto existing components without altering their underlying structure.
Installation
Prerequisites:
- React 16.8 or higher
- Node.js 12.х or higher
- TypeScript 4.9 or higher
You can use any package manager:
npm add features-applier
Usage
Below is an example of how to apply some features to a React component:
import { applyFeatures } from "features-applier";
// Define a simple component that displays a message
const GreetingComponent = ({ name, greeting, ...props }) => (
<div {...props}>
{greeting}, {name}!
</div>
);
// Define a hook that adds greeting
// time based on the current hour to the props
const useTimeOfDayGreeting = (props) => {
const hour = new Date().getHours();
const timeOfDay = hour < 12 ? "Morning" : hour < 18 ? "Afternoon" : "Evening";
return { ...props, greeting: `Good ${timeOfDay}` };
};
// Define a HOC that adds styling to the component
const withStyling = (Component) => (props) =>
<Component {...props} style={{ color: "blue", fontWeight: "bold" }} />;
// Apply enhancements to the GreetingComponent using applyFeatures
const EnhancedGreetingComponent = applyFeatures<{ greeting: never }>(
(builder) => {
builder.applyHOCs(withStyling).applyHooks(useTimeOfDayGreeting);
}
)(GreetingComponent);
// Usage in your application
const App = () => <EnhancedGreetingComponent name="Username" />;
render(<App />, document.getElementById("root"));
Expected output in your application as follows:
<div id="root">
<div style="color: blue; font-weight: bold">Good Morning, Username!</div>
</div>
Advanced Usage
Features Applier includes these APIs
applyFeatures()
: powerful tool for enhancing components by applying specified features. It provides a structured way to integrate enhancements such as hooks and higher-order components (HOCs).buildFeatures()
: utility function that helps to encapsulate and modularize enhancements. This function is particularly useful for creating reusable feature sets that can be applied across different components within an application.buildFeaturesApplier()
: builder method that allows to assemble any set ofappliers
,modifiers
, andbuilder
behaviors, allowing for extensive customization of how features are applied. Can serve as a foundational tool for building adaptable and scalable feature applications, accommodating a wide array of application needs beyond standard React component enhancement.
Conditional Feature Application
Using filtered
modifier, you can dynamically apply hooks and HOCs based on the props or the component's state:
const UserComponent = ({ user }) => <div>Welcome, {user}!</div>;
const useAdminFeatures = (props) => {
return { ...props, adminProp: "Admin features active" };
};
const withAuthentication = (Component) => (props) => {
return props.isAuthenticated ? (
<Component {...props} />
) : (
<div>Please log in</div>
);
};
// Enhance the component conditionally
const EnhancedUserComponent = applyFeatures<{ isAuthenticated: boolean }>(
(builder) => {
builder
// Always apply the authentication HOC
.applyHOCs(withAuthentication)
// Conditionally apply the admin hook
// based on the condition in the first argument
.applyHooks.filtered((props) => props.user === "Admin", useAdminFeatures);
}
)(UserComponent);
// Usage of the enhanced component
const App = () => <EnhancedUserComponent isAuthenticated={true} user="Admin" />;
Alternative Builder Mode
The builder
supports an alternative mode of applying modifiers that is especially beneficial for integrating complex modifications logic by chaining multiple modifiers in an explicit order.
const EnhancedComponent = applyFeatures((builder) => {
builder("sequential")
.applyHooks.filtered((props) => props.isAdmin)
.debounced(300)
.throttled(500)
// Applying enhancements now in the run function
.run(useEnhanceHook);
})(BasicComponent);
Note: The modifiers
debounced
andthrottled
are conceptual illustrations and not implemented in the current version of Features Applier. You can add it on your own; see Advanced Customization for more information
Creating Reusable Features
You can use buildFeatures
to create standalone features that encapsulate specific enhancements, such as higher-order components (HOCs) or hooks. These feature sets can then be applied to components through applyFeatures
.
import React from "react";
import { applyFeatures, buildFeatures } from "../src";
const GreetingComponent = ({ name, greeting, ...props }) => (
<div {...props}>
{greeting}, {name}!
</div>
);
const useTimeOfDayGreeting = (props) => {
const hour = new Date().getHours();
const timeOfDay = hour < 12 ? "Morning" : hour < 18 ? "Afternoon" : "Evening";
return { ...props, greeting: `Good ${timeOfDay}` };
};
const withStyling = (Component) => (props) =>
<Component {...props} style={{ color: "blue", fontWeight: "bold" }} />;
// Build reusable styling feature
const styling = buildFeatures((builder) => {
builder.applyHOCs(withStyling);
});
// Apply enhancements to the GreetingComponent
const EnhancedGreetingComponent =
applyFeatures <
{ greeting: never } >
((builder) => {
builder.use(styling).applyHooks(useTimeOfDayGreeting);
})(GreetingComponent);
// Usage in your application
const App = () => <EnhancedGreetingComponent name="Username" />;
Advanced Customization
The buildFeaturesApplier
method allows you to set up a custom feature application environment by defining custom runners
, appliers
and modifiers
. This setup can be aligned with unique project requirements, enabling a more granular control over how features are applied across various elements.
Here’s how to create a custom instance of applyFeatures
using buildFeaturesApplier
to provide specific behavior that is not covered by the default setup:
import { createFeaturesApplier } from "features-applier";
// Custom configuration for a features applier
const { applyFeatures: customApplyFeatures } = buildFeaturesApplier()
.addModifiers(/* Custom modifiers */)
.addAppliers(/* Custom appliers */)
.addHelpers({
/* Custom functions */
})
.createRunners(() => [
/* Custom runners logic */
])
.finish({
defaultRunner: "direct",
});
Example: Creating a Custom Runner
Let's create a custom runner:
import { buildFeaturesApplier } from "features-applier";
const getCustomRunners = () =>
[
{
name: "simple",
build: ({
builder,
runConfig,
setRunConfig,
helpers: { createApplierConfig },
}) => {
type SimpleBuilder = {
applyAny: (...items: any[]) => SimpleBuilder;
};
return {
applyAny: (...items: any[]) => {
setRunConfig({
appliers: [
...runConfig.appliers,
createApplierConfig(
{
name: "any",
apply: pipeline,
},
{
params: items,
}
),
],
});
// Return the builder for chaining
return builder;
},
} as SimpleBuilder;
},
},
] as const;
const core = buildFeaturesApplier.getDefaults();
const { applyFeatures } = buildFeaturesApplier()
.addPlugin(core.defaultPlugin)
.createRunners(getCustomRunners)
.finish();
Now, you can use your simple runner as follows:
const useRawEnhancement = (Component) => (props) => {
return <Component {...props} />;
};
const EnhancedComponent = applyFeatures((builder) => {
builder("simple").applyAny(useRawEnhancement);
})(BasicComponent);
For more detailed examples, please see the src/models/core directory of this project.