Riot.js сочетает декларативный подход к построению интерфейсов с возможностями функционального программирования. Это позволяет создавать компоненты, которые легко тестировать, масштабировать и сопровождать.
В Riot.js каждый компонент по сути является функцией, принимающей
свойства (props) и возвращающей интерфейс, описанный через
шаблон. Такой подход соответствует принципу чистых функций:
Пример базового функционального компонента:
import { component } from 'riot';
const Greeting = component({
props: {
name: 'Гость'
},
template: `<p>Привет, {props.name}!</p>`
});
Здесь Greeting ведет себя как чистая функция: при
одинаковых props результат будет всегда одинаков.
Функциональный подход подразумевает минимизацию мутаций. В Riot.js
состояние компонента (state) можно хранить в объекте, но
его изменение рекомендуется производить через создание нового
объекта, а не через прямое присваивание:
this.state = { count: 0 };
increment() {
this.update({ state: { ...this.state, count: this.state.count + 1 } });
}
Использование оператора распространения (...) сохраняет
неизменяемость исходного состояния, что упрощает отладку и интеграцию с
инструментами типа Redux или Immer.
Riot.js поощряет композицию компонентов. Вместо создания одного большого компонента рекомендуется делить интерфейс на малые, независимые функции-компоненты:
const Button = component({
props: { label: '' },
template: `<button>{props.label}</button>`
});
const App = component({
template: `
<div>
<Button label="Нажми меня" />
<Button label="Или меня" />
</div>
`
});
Каждый компонент решает отдельную задачу. Такой подход улучшает читаемость и тестируемость кода.
Riot.js позволяет создавать вычисляемые свойства, которые зависят от состояния, но не изменяют его напрямую:
const Counter = component({
state: { count: 0 },
getters: {
doubled() {
return this.state.count * 2;
}
},
template: `
<div>
<p>Count: {state.count}</p>
<p>Doubled: {getters.doubled}</p>
<button oncl ick={increment}>+</button>
</div>
`,
increment() {
this.update({ state: { ...this.state, count: this.state.count + 1 } });
}
});
getters.doubled представляет собой чистую функцию,
вычисляющую значение без изменения состояния. Это полностью
соответствует принципам функционального программирования.
Riot.js поддерживает паттерны функционального программирования, такие как композиция функций высшего порядка (HOF). Компоненты можно оборачивать для добавления дополнительного поведения:
function withLogging(component) {
return component.extend({
onMounted() {
console.log('Компонент смонтирован');
}
});
}
const LoggedButton = withLogging(Button);
Такой подход позволяет добавлять функциональность без изменения исходного компонента, обеспечивая чистоту и модульность кода.
При работе с массивами элементов предпочтительно использовать
иммутабельные операции (map,
filter, reduce) вместо мутаций:
const TodoList = component({
state: {
todos: ['Купить хлеб', 'Прочитать книгу']
},
addTodo(todo) {
this.update({ state: { ...this.state, todos: [...this.state.todos, todo] } });
},
template: `
<ul>
{state.todos.map(todo => <li>{todo}</li>)}
</ul>
`
});
Такой подход исключает побочные эффекты и упрощает работу с виртуальным DOM.
Riot.js позволяет обрабатывать события через функции обратного вызова, что гармонично сочетается с функциональным стилем:
const CounterButton = component({
state: { count: 0 },
template: `<button oncl ick={increment}>{state.count}</button>`,
increment() {
this.update({ state: { ...this.state, count: this.state.count + 1 } });
}
});
Каждое событие запускает функцию, которая возвращает новый стейт, сохраняя иммутабельность.
map, filter, reduce и функции
обратного вызова.Такой стиль обеспечивает предсказуемость поведения компонентов, упрощает тестирование и масштабирование приложений на Riot.js.