Why did I decide to use the VAC pattern in my project?
Recently, I learned about the concept of separation of concerns and began studying various design patterns in-depth. Among them, I found the VAC (View-Assets-Component) pattern to be particularly suitable for separating and managing the interests of business logic and views in a systematic manner.
In practical work, UI developers and FE developers are sometimes separated, and their areas of work often overlap. Even though there were no dedicated UI developers in my project, I wanted to use a design pattern that could be applied in practical settings as well.
For these reasons, I introduced the VAC pattern to this project. In this article, I will share the advantages and disadvantages we encountered while applying the VAC pattern, as well as provide an introduction to the pattern itself.
Then let’s find out what the VAC pattern is first.
What is a VAC pattern?
The View Asset Component (VAC) pattern is a design method that aims to minimize the coupling between View Logic and JSX. It abstracts the JSX area into a ‘Props Object’ in View components and separates JSX into VAC. The VAC pattern follows the following rules:
- Only perform actions related to style control rendering, such as repetition, conditional exposure, etc.
- A stateless component that is controlled only through Props and does not manage its own state.
- Bind functions directly to events without further action.
While VAC components cannot have state, it is possible to have a component with state as a child. In this case, the VAC pattern does not intervene between the parent and child components but simply serves to deliver Props.
Compared to existing patterns, the VAC pattern provides a cleaner separation of concerns and makes it easier to reason about the codebase. By separating business logic and rendering concerns, it becomes easier to test and maintain the code. Additionally, the VAC pattern promotes the reuse of components, as they are designed to be stateless and modular.
Let’s compare the differences from the existing patterns.
Existing Pattern
- Handle
JSXandstylewithin the component and bind the necessary callback functions related to the business logic. - Even if
PropsorStateis simply changed, if a UI developer has made changes to theJSXarea, there may be code conflicts when modifying theJSXarea.
Let’s take a look at the structure of the VAC pattern to overcome these drawbacks.
VAC Pattern
- Split
JSXinto separate components to minimize the disadvantages of the existing pattern. - Manage the passed
PropsandStateas aProps Objectto make it easy to check the required attributes and callback functions forVAC.
In other words, it can be considered a design pattern that provides a guide on how to separate View logic and JSX.
I have applied this structure to my project.
Applying to a Project
View Component
const Sidebar = () => {
const [selectPage, setSelectPage] = useState<"myArt" | "createArt">("myArt");
const navigate = useNavigate();
const onNavigateGallery = () => {
setSelectPage("myArt");
navigate(ADMIN_ROUTE.MY_ART);
};
const onNavigateCreate = () => {
setSelectPage("createArt");
navigate(ADMIN_ROUTE.CREATE_ART);
};
const sideBarViewProps = {
onNavigateGallery,
onNavigateCreate,
selectPage
};
// Using JSX as VAC
return <SidebarView {...sideBarViewProps} />; // Props Object
};
export default Sidebar;
In the View component, functions, props, state, and callback are implemented and delivered to VAC Component through a Props Object.
If View logic is reused, it can be managed separately by HOC or Hook.
VAC Component
const SidebarView = ({
onNavigateGallery,
onNavigateCreate,
selectPage
}: TSideBarViewProps) => {
return (
<Container>
...
</Container>
);
};
In the VAC component, only JSX is used.
Since the JSX area always uses Props as the received value, UI developers do not need to understand how Props are configured. Only the delivered Props can be used to develop JSX.
There are some precautions to consider when applying VAC to a project.
Things to be cautious about
VAC should not be involved in the functionality or state control of the View component as follows
// View Component
const SpinBox = () => {
const [value, setValue] = useState(0);
const props = {
value,
step: 1,
handleClick: (n) => setValue(n),
};
// Inappropriate VAC behavior of controlling value
return <SpinBoxView {...props} />;
};
The correct VAC only binds the handler to the event and does not interfere with what to do.
// VAC
const SpinBoxView = ({ value, onIncrease, onDecrease }) => (
<div>
<button onClick={onDecrease}>-</button>
<span>{value}</span>
<button onClick={onIncrease}>+</button>
</div>
);
Scenario in Practical Work
- The FE developer modifies the logic in the
Props Objectof theViewcomponent locally. - The UI developer applies
stylein theVAC. - The FE developer modifies the functionality in the View component, and the UI developer modifies the
JSXin theVACto avoid conflicts.
Naming of VAC props
The props should be named in a way that is intuitive for rendering, rather than being data-centric.
For example:
- Use
disabledDecreaseanddisabledIncreaseinstead ofisMaxandisMin. - Use
showEditButton: isLogin && isOwnerinstead of passingisLoginandisOwnerseparately and checkingisLogin && isOwnerin the VAC.
This makes it easy to infer the role of the props in rendering.
Difference between VAC Pattern and Presentational Container Pattern
The VAC pattern is a type of Presentational and Container component pattern, as it delegates the logic to the container component.
However, the difference between the two patterns lies in whether the components can have View logic (UI functionality, state management) or not.
Presentational Components
- Aim to separate business logic and View concerns.
- Manage business logic in the container components and control the Presentational components.
- Presentational components handle View logic (UI functionality, state management) and rendering.
VAC
- Aims to separate View logic (UI functionality, state management) and rendering (JSX) concerns.
- The View component serves as the container component for
VACand manages aProps Objectthat abstracts the JSX for the VAC. - VAC manages to render with
JSXandStyle.
VAC provides more specific criteria than Presentational components and helps to achieve consistent design from the perspective of the component that handles JSX rendering.
Review after applying the VAC pattern
Pros
- Business logic and UI are separated, making it easy to understand the code.
- It is easy to make changes. If you want to change the CSS library, you only need to change the VAC component.
- VAC components can be designed and styled based solely on the props interface that is passed down. You don’t need to worry about how the props are passed down.
Cons
- There is often drilling of props, which can be inconvenient.
- It is not easy to apply the pattern when directly manipulating the DOM.
- Props can become unwieldy.
Due to the distinction between business logic and UI, props can become bulky.
- The VAC component criteria are ambiguous.
Review
Even simple components require a separation of logic and style, which can lead to confusion.
After learning about the VAC pattern and applying it to a project, I think that every design pattern has its own pros and cons. However, if you think in terms of the separation of concerns or collaboration with UI developers, the separation of business logic and UI is very suitable for the purpose.
I think the VAC pattern is a React design pattern worth studying and applying at least once.
References
React VAC Pattern — View 로직과 JSX의 의존성을 최소화 하자!
