thumbnamil

VAC Pattern in React

Jul 4, 2023

  • Design Pattern

  • React

Vac Pattern Application Review

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

image

  • Handle JSX and style within the component and bind the necessary callback functions related to the business logic.
  • Even if Props or State is simply changed, if a UI developer has made changes to the JSX area, there may be code conflicts when modifying the JSX area.

Let’s take a look at the structure of the VAC pattern to overcome these drawbacks.

VAC Pattern

image

  • Split JSX into separate components to minimize the disadvantages of the existing pattern.
  • Manage the passed Props and State as a Props Object to make it easy to check the required attributes and callback functions for VAC.

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, propsstate, 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

  1. The FE developer modifies the logic in the Props Object of the View component locally.
  2. The UI developer applies style in the VAC.
  3. The FE developer modifies the functionality in the View component, and the UI developer modifies the JSX in the VAC to 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 disabledDecrease and disabledIncrease instead of isMax and isMin.
  • Use showEditButton: isLogin && isOwner instead of passing isLogin and isOwner separately and checking isLogin && isOwner in 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 VAC and manages a Props Object that abstracts the JSX for the VAC.
  • VAC manages to render with JSX and Style.

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

[리액트] VAC 패턴 적용 후기 및 장단점

React VAC Pattern — View 로직과 JSX의 의존성을 최소화 하자!

프레임워크의 선택, React vs Angular

React에서 View의 렌더링 관심사 분리를 위한 VAC 패턴 소개 | WIT블로그

npm: react-vac