Интеграция с React экосистемой

Mithril — это компактный и высокопроизводительный фреймворк для создания клиентских приложений на JavaScript. Несмотря на собственный подход к компонентам и виртуальному DOM, иногда возникает необходимость интегрировать его с инструментами и библиотеками, характерными для React-экосистемы. Рассмотрим основные методы и практики такой интеграции.


Использование React-библиотек внутри Mithril

React-библиотеки предоставляют широкий спектр UI-компонентов, управления состоянием и маршрутизации. Mithril поддерживает интеграцию с React-компонентами через обёртку в виде кастомного компонента. Принцип работы основан на монтаже React-компонента в DOM-элемент, управляемый Mithril.

Пример обёртки React-компонента в Mithril:

import m from "mithril";
import React from "react";
import ReactDOM from "react-dom";
import { Button } from "@mui/material"; // React-библиотека

const ReactButtonWrapper = {
    oncreate: ({ dom }) => {
        ReactDOM.render(<Button variant="contained">Click me</Button>, dom);
    },
    onremove: ({ dom }) => {
        ReactDOM.unmountComponentAtNode(dom);
    },
    view: () => m("div") // Mithril создаёт контейнер для React
};

m.mount(document.body, {
    view: () => m(ReactButtonWrapper)
});

Ключевые моменты:

  • Mithril управляет контейнером <div>, внутри которого React-компонент рендерится через ReactDOM.render.
  • onremove обеспечивает корректное удаление React-компонента, предотвращая утечки памяти.
  • Такой подход позволяет использовать любые React-компоненты без модификации их кода.

Управление состоянием через Redux

Для интеграции с Redux или другими React-ориентированными менеджерами состояния применяется подписка на изменения состояния с последующим обновлением Mithril-компонентов.

Пример:

import { createStore } from "redux";
import m from "mithril";

const reducer = (state = { count: 0 }, action) => {
    switch (action.type) {
        case "INCREMENT": return { count: state.count + 1 };
        default: return state;
    }
};

const store = createStore(reducer);

const Counter = {
    oninit: ({ state }) => {
        state.unsubscribe = store.subscribe(() => m.redraw());
    },
    onremove: ({ state }) => {
        state.unsubscribe();
    },
    view: () => m("div", [
        m("p", `Count: ${store.getState().count}`),
        m("button", { onclick: () => store.dispatch({ type: "INCREMENT" }) }, "Increment")
    ])
};

m.mount(document.body, Counter);

Особенности интеграции:

  • store.subscribe уведомляет Mithril о необходимости перерисовки через m.redraw().
  • Mithril сохраняет привычную декларативную работу с DOM, одновременно используя мощь Redux.

Использование хуков React в Mithril

Прямое использование React-хуков невозможно, так как Mithril не опирается на виртуальные хуки. Однако возможно разделение логики состояния в кастомные функции, совместимые с обоими фреймворками.

Пример создания универсального состояния:

function useCounter(initial = 0) {
    let value = initial;
    const increment = () => { value++; };
    const get = () => value;
    return { increment, get };
}

// Mithril-компонент
const Counter = {
    oninit: ({ state }) => {
        state.counter = useCounter(0);
    },
    view: ({ state }) => m("div", [
        m("p", `Count: ${state.counter.get()}`),
        m("button", { onclick: state.counter.increment }, "Increment")
    ])
};

Такой подход позволяет дублировать логику между React и Mithril, сохраняя синхронизацию состояния на уровне функций.


Интеграция маршрутизации

React-экосистема активно использует react-router. В Mithril есть собственный маршрутизатор, но возможна интеграция через:

  • Контейнер для React-роутера: Mithril рендерит контейнер, внутри которого работает BrowserRouter от React.
  • Синхронизация URL: Mithril и React могут обмениваться информацией о текущем пути через события window.history и popstate.

Пример базовой интеграции:

import { BrowserRouter, Route, Routes } from "react-router-dom";
import ReactDOM from "react-dom";
import m from "mithril";
import React from "react";

const ReactRouterWrapper = {
    oncreate: ({ dom }) => {
        ReactDOM.render(
            <BrowserRouter>
                <Routes>
                    <Route path="/" element={<div>Home</div>} />
                    <Route path="/about" element={<div>About</div>} />
                </Routes>
            </BrowserRouter>,
            dom
        );
    },
    onremove: ({ dom }) => ReactDOM.unmountComponentAtNode(dom),
    view: () => m("div")
};

m.mount(document.body, ReactRouterWrapper);

Особенности:

  • Mithril управляет контейнером маршрутизатора.
  • Все навигации React корректно рендерятся внутри Mithril.

Подключение UI-библиотек и CSS-in-JS

Mithril не имеет собственного решения для CSS-in-JS, но поддерживает внешние библиотеки:

  • Emotion или Styled Components могут использоваться в React-компонентах внутри Mithril.
  • Прямое подключение CSS через import работает для глобальных стилей.

Пример использования Styled Components в обёртке React:

import styled from "styled-components";
import React from "react";
import ReactDOM from "react-dom";

const StyledButton = styled.button`
  background-color: #007bff;
  color: white;
  padding: 0.5em 1em;
  border: none;
  border-radius: 4px;
`;

const ButtonWrapper = {
    oncreate: ({ dom }) => {
        ReactDOM.render(<StyledButton>Click me</StyledButton>, dom);
    },
    onremove: ({ dom }) => ReactDOM.unmountComponentAtNode(dom),
    view: () => m("div")
};

Оптимизация производительности

  • Использовать разделение контейнеров React по компонентам, чтобы не рендерить весь UI через React.
  • Применять m.redraw.strategy("diff") при частом обновлении данных.
  • Сохранять минимальное количество React-обёрток для крупных библиотек UI.

Выводы по интеграции

  • Mithril и React могут сосуществовать через контейнерные обёртки.
  • Управление состоянием через Redux или кастомные функции упрощает синхронизацию между фреймворками.
  • React-хуки не применяются напрямую, но логику состояния можно адаптировать.
  • Интеграция маршрутизации и UI-библиотек требует контейнерного подхода, обеспечивающего независимость жизненных циклов.

Такой подход позволяет использовать мощные инструменты React без отказа от лёгкости и быстродействия Mithril.