← Назад к вопросам

Какие знаешь паттерны переиспользования кода в React?

1.7 Middle🔥 191 комментариев
#React#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Паттерны переиспользования кода в React

В React существует несколько фундаментальных паттернов для переиспользования кода, которые эволюционировали вместе с развитием библиотеки. Эти подходы позволяют создавать более поддерживаемые, тестируемые и масштабируемые приложения.

1. Компонентный подход (Component Composition)

Базовый принцип React — композиция компонентов. Вместо создания монолитных компонентов, мы разбиваем интерфейс на мелкие переиспользуемые части.

// Базовый переиспользуемый компонент кнопки
const Button = ({ children, variant = 'primary', onClick }) => {
  const baseClasses = 'px-4 py-2 rounded font-medium';
  const variants = {
    primary: 'bg-blue-600 text-white hover:bg-blue-700',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-600 text-white hover:bg-red-700'
  };
  
  return (
    <button 
      className={`${baseClasses} ${variants[variant]}`}
      onClick={onClick}
    >
      {children}
    </button>
  );
};

// Использование
const App = () => (
  <div>
    <Button variant="primary">Сохранить</Button>
    <Button variant="secondary">Отмена</Button>
  </div>
);

2. Higher-Order Components (HOC)

HOC — это функция, которая принимает компонент и возвращает новый компонент с дополнительной функциональностью. Этот паттерн был особенно популярен до появления хуков.

// HOC для добавления логики аутентификации
const withAuth = (WrappedComponent) => {
  const AuthenticatedComponent = (props) => {
    const [isAuthenticated, setIsAuthenticated] = useState(false);
    
    useEffect(() => {
      // Проверка аутентификации
      checkAuth().then(setIsAuthenticated);
    }, []);
    
    if (!isAuthenticated) {
      return <div>Пожалуйста, войдите в систему</div>;
    }
    
    return <WrappedComponent {...props} />;
  };
  
  // Для отладки в DevTools
  AuthenticatedComponent.displayName = `WithAuth(${WrappedComponent.displayName || WrappedComponent.name})`;
  
  return AuthenticatedComponent;
};

// Использование
const UserProfile = ({ user }) => <div>Профиль: {user.name}</div>;
const ProtectedUserProfile = withAuth(UserProfile);

3. Render Props

Паттерн Render Props предполагает передачу функции в качестве пропса, которая возвращает React-элемент. Это позволяет компонентам делиться своей внутренней логикой.

// Компонент с render prop для отслеживания мыши
class MouseTracker extends React.Component {
  state = { x: 0, y: 0 };
  
  handleMouseMove = (event) => {
    this.setState({
      x: event.clientX,
      y: event.clientY
    });
  };
  
  render() {
    return (
      <div onMouseMove={this.handleMouseMove}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// Использование
const App = () => (
  <MouseTracker render={({ x, y }) => (
    <div>
      Координаты мыши: {x}, {y}
    </div>
  )} />
);

4. Кастомные хуки (Custom Hooks)

С появлением хуков в React 16.8, кастомные хуки стали предпочтительным способом переиспользования логики с состоянием. Они позволяют извлекать и повторно использовать логику состояния между компонентами.

// Кастомный хук для работы с localStorage
const useLocalStorage = (key, initialValue) => {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(error);
      return initialValue;
    }
  });
  
  const setValue = (value) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.error(error);
    }
  };
  
  return [storedValue, setValue];
};

// Использование в разных компонентах
const ThemeToggle = () => {
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };
  
  return (
    <button onClick={toggleTheme}>
      Переключить тему: {theme}
    </button>
  );
};

5. Компоненты-провайдеры (Provider Pattern)

Используется в связке с Context API для передачи данных через дерево компонентов без явной передачи пропсов на каждом уровне.

// Создание контекста
const ThemeContext = React.createContext();

// Провайдер
const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light');
  
  const toggleTheme = () => {
    setTheme(prev => prev === 'light' ? 'dark' : 'light');
  };
  
  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

// Кастомный хук для использования контекста
const useTheme = () => {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme должен использоваться внутри ThemeProvider');
  }
  return context;
};

6. Компоненты как дети (Children Pattern)

Использование props.children или именованных слотов для создания гибких компонентов-контейнеров.

// Карточка с заголовком и контентом
const Card = ({ title, children, footer }) => (
  <div className="card">
    {title && <div className="card-header">{title}</div>}
    <div className="card-body">{children}</div>
    {footer && <div className="card-footer">{footer}</div>}
  </div>
);

// Использование
const UserCard = ({ user }) => (
  <Card 
    title={user.name}
    footer={<button>Редактировать</button>}
  >
    <p>Email: {user.email}</p>
    <p>Роль: {user.role}</p>
  </Card>
);

Критерии выбора паттерна

При выборе паттерна для переиспользования кода следует учитывать:

  • Сложность логики: Для простой логики — компоненты, для сложной — кастомные хуки
  • Необходимость в состоянии: HOC и render props для классовых компонентов, кастомные хуки — для функциональных
  • Гибкость vs простота: Render props предлагают больше гибкости, HOC — более простой API
  • Производительность: Context API может вызывать лишние ререндеры, что требует оптимизации
  • Типизация: Каждый паттерн имеет свои особенности при работе с TypeScript

Современные тенденции

В современной экосистеме React наблюдается смещение в сторону:

  • Кастомных хуков как основного способа переиспользования логики
  • Компонентной композиции через children и slots
  • Библиотек состояний (Zustand, Jotai) для сложных сценариев
  • Server Components для разделения клиентской и серверной логики

Каждый паттерн имеет свои сильные стороны и оптимальные сценарии применения. Ключ к эффективному переиспользованию кода — понимание, когда какой подход использовать, и комбинирование паттернов для решения конкретных задач.