Mithril — это компактный и высокопроизводительный фреймворк для создания клиентских приложений на JavaScript. Несмотря на собственный подход к компонентам и виртуальному DOM, иногда возникает необходимость интегрировать его с инструментами и библиотеками, характерными для React-экосистемы. Рассмотрим основные методы и практики такой интеграции.
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)
});
Ключевые моменты:
<div>, внутри
которого React-компонент рендерится через
ReactDOM.render.onremove обеспечивает корректное удаление
React-компонента, предотвращая утечки памяти.Для интеграции с 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().Прямое использование 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 есть собственный маршрутизатор, но возможна интеграция
через:
BrowserRouter от
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 не имеет собственного решения для CSS-in-JS, но поддерживает внешние библиотеки:
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")
};
m.redraw.strategy("diff") при частом
обновлении данных.Такой подход позволяет использовать мощные инструменты React без отказа от лёгкости и быстродействия Mithril.