Can Design Patterns Make Your React Hooks More Elegant?

This article explores how classic design patterns and solid principles can be applied to React Hooks development, demonstrating with Context, custom global state, useState vs useReducer, and custom hook composition to improve code maintainability and reduce unnecessary re‑renders.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
Can Design Patterns Make Your React Hooks More Elegant?

1. Introduction

Design patterns are often discussed in object‑oriented languages, but their relevance to frontend JavaScript and React Hooks is less explored. After reading JavaScript Design Patterns and Development Practice , the author investigates whether design patterns can help write more elegant Hooks.

2. Why Design Patterns

Design patterns are like basic skills for developers, similar to data structures, providing stable, maintainable code. Hooks increase maintenance cost, especially for teams with varying skill levels, because they lack the lifecycle constraints of class components. Custom hooks can become hard to maintain if not designed well.

3. Classic Design Patterns

Brief overview of SOLID principles and common patterns such as Singleton, Strategy, Proxy, Iterator, Publish‑Subscribe, Command, Composite, Template Method, Mediator, Decorator, State, Adapter, etc.

4. 1 + 1 > 2

Using React useContext for global state causes all components under the provider to re‑render whenever the state changes, even if they do not consume the context. This resembles the Publish‑Subscribe pattern, so a custom global state object (GrayState) with attach / detach can be used to notify only interested components.

// context.js
const GrayContext = React.createContext();
export default GrayContext;

// App.js
import GrayContext from './context';
function App() {
  console.log('App rerender');
  const [globalStatus, setGlobalStatus] = useState({});
  useEffect(() => {
    console.log('Get GrayState');
    setTimeout(() => {
      setGlobalStatus({ gray: true });
    }, 1000);
  }, []);
  return (
    <GrayContext.Provider value={globalStatus}>
      <GrayComponent />
      <OtherChild />
    </GrayContext.Provider>
  );
}

// GrayComponent/index.js
function GrayComponent() {
  console.log('GrayComponent rerender');
  const grayState = useContext(GrayContext);
  return (
    <div>
      子节点
      {grayState.gray && <div>灰度字段</div>}
    </div>
  );
}

// OtherChild/index.js
function OtherChild() {
  console.log('OtherChild rerender');
  return <div>其它子节点</div>;
}

Using the custom GrayState instance reduces unnecessary re‑renders compared to Context.

5. useState vs useReducer

Discussion of when useReducer is preferable, illustrated with a music player mode switch example. Simple if…else works for a few states, but as branches grow, the State pattern (or useReducer) offers clearer logic.

// Simple mode switch with useState
function Mode() {
  const [mode, setMode] = useState('order');
  const changeHandle = useCallback((mode) => {
    if (mode === 'order') {
      console.log('切换到随机模式');
      setMode('random');
    } else if (mode === 'random') {
      console.log('切换到循环模式');
      setMode('loop');
    } else if (mode === 'loop') {
      console.log('切换到顺序模式');
      setMode('order');
    }
  }, []);
  return (
    <div>
      <Button onClick={() => changeHandle(mode)}>切换模式</Button>
      <div>{mode}</div>
    </div>
  );
}

// Same logic with useReducer
const reducer = (state) => {
  switch (state) {
    case 'order':
      console.log('切换到随机模式');
      return 'random';
    case 'random':
      console.log('切换到循环模式');
      return 'loop';
    case 'loop':
      console.log('切换到顺序模式');
      return 'order';
  }
};
function Mode() {
  const [mode, dispatch] = useReducer(reducer, 'order');
  return (
    <div>
      <Button onClick={dispatch}>切换模式</Button>
      <div>{mode}</div>
    </div>
  );
}

6. Principles for Custom Hook Encapsulation

Custom hooks should follow the single‑responsibility principle and be data‑driven. A bad example ( useTodayFirstOpen) mixes status handling, first‑open logic, and UI side‑effects, making maintenance difficult. Proper separation into focused hooks improves modularity.

// User status management
function useUserStatus() {
  const [status, setStatus] = useState();
  const fetchStatus = async () => {
    const res = await getUserStatus();
    setStatus(res);
  };
  useEffect(() => { fetchStatus(); }, []);
  const updateStatus = useCallback(async (type, val) => {
    const res = await updateUserStatus(type, val);
    if (res) {
      console.log('设置成功');
      fetchStatus();
    } else {
      console.log('设置失败');
    }
  }, []);
  return [status, updateStatus];
}

// Second‑confirm logic
function useSecondConfirm(key, gapDay, confirmOptions = {}) {
  const [isConfirm, setIsConfirm] = useState(false);
  const showConfirm = useCallback(() => {
    const curr = Date.now();
    const lastDay = window.localStorage.getItem(`${key}_lastCheckDay`);
    if (!lastDay || diffDay(curr, lastDay) > gapDay) {
      setTimeout(async () => {
        tryToPopConfirm({
          title: confirmOptions.title,
          content: confirmOptions.content,
          onConfirm: () => setIsConfirm(true),
        });
      }, 300);
      window.localStorage.setItem(`${key}_lastCheckDay`, curr);
    }
  }, [gapDay]);
  return [isConfirm, showConfirm];
}

// Composed hook
function useStatusWithSecondConfirm(type, gapDay, confirmOptions) {
  const [userStatus, setUserStatus] = useUserStatus();
  const [isConfirm, showConfirm] = useSecondConfirm(type, gapDay, confirmOptions);
  useEffect(() => {
    if (userStatus && userStatus[type] <= 0) {
      showConfirm();
    }
  }, [userStatus]);
  useEffect(() => {
    if (isConfirm) {
      setUserStatus(type, 1);
    }
  }, [isConfirm]);
  return [userStatus ? userStatus[type] : null, setUserStatus];
}

7. Summary

Design patterns and solid principles guide developers to write clearer, more maintainable React Hook code. While Hooks are powerful, applying appropriate patterns can mitigate maintenance challenges and help build elegant, scalable frontend applications.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

State ManagementhooksCustom Hook
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.