Debido al hecho de que React proporciona capacidades sorprendentes para trabajar con pantalla, solo puede centrarse en organizar la lógica de la aplicación y la semántica del código que describe cómo trabajar con datos. Aquellos. Al elegir una biblioteca de administración de estado, se realiza la elección del estilo de la base de código futura.
En este artículo, consideraremos un enfoque basado en la tienda de servicios implementado a través de una biblioteca lamp-luwak.

TL; DR Quien quiera comenzar a escribir código pronto puede pasar al siguiente párrafo.
Toda la lógica dentro de su aplicación se divide en servicios, cada servicio contiene un solo lado, cuyo cambio notifica a los suscriptores. El servicio también contiene funciones lógicas, donde puede acceder a otros servicios y reescribir el lado inmutable, estas funciones pueden ser asíncronas y contener efectos secundarios.
lamp-luwak , . , React useProvide useSubscribe . , , .
, - . , embedded . .
subscribe. .
, todo- :
todos create-react-app
npx create-react-app todos --template typescript --use-npm
yarn create react-app todos --template typescript
lamp-luwak todos
npm i --save lamp-luwak
yarn add lamp-luwak
, React - , - .
— -, , .
-, store.
class Todos {
store = [ ];
store . .
class TodoCounters {
todo = provide(Todo);
provide , , Todo.
React
const List = () => {
const todo = useProvide(Todo);
};
-. React useProvide, — - , Todo. , .
— , , store provide useProvide / , .
, List Todo.
services, React components .
sr/
components/ // React
Counters.tsx //
Input.tsx //
Task.tsx //
List.tsx //
services/ //
Todo/
Task.ts //
Todo.ts //
TodoCounters.ts //
App.tsx //
2- . :
Todo — , add ;TodoCounters — .
Todo , Task, .. , , . , , . , Date, Map, Set. ( SSR) , UI. , . , Task toggle, .
Task, , create, -, - .
import { create } from 'lamp-luwak';
import { Task } from './Todo/Task';
export class Todo {
store = [
create(Task, { id: 1, label: 'Cook the dinner', completed: false }),
create(Task, { id: 2, label: 'Cook the breakfast', completed: true })
]
add(label: string) {
this.store = this.store.concat(
create(Task, { id: Date.now(), label, completed: false })
);
}
}
Todo. store Task, create. add, , Task. id, Date.now.
import { subscribe, modify, action } from 'lamp-luwak';
type Store = {
id: number,
label: string,
completed: boolean
}
export const TaskChanged = action();
export class Task {
store: Store;
constructor(store: Store) {
this.store = store;
subscribe(this, TaskChanged);
}
toggle() {
modify(this).completed = !this.store.completed;
}
}
Task, . : id , label completed. toggle, , modify, , .
TaskChanged, action.
— , . , — dispatch lamp-luwak.
, , TaskChanged Task. subscribe, — , .. , — , . TaskChanged, .
import { provide, subscribe } from 'lamp-luwak';
import { Todo } from './Todo';
import { TaskChanged } from './Todo/Task';
export class TodoCounters {
todo = provide(Todo);
store = {
active: 0,
completed: 0
}
constructor() {
subscribe(this.todo, this.calculate, this);
subscribe(TaskChanged, this.calculate, this);
this.calculate();
}
calculate() {
const items = this.todo.store;
const completed = items.filter(item => item.store.completed).length;
const active = items.length - completed;
this.store = { completed, active };
}
}
TodoCounters, . 2- :
Todo, , .TaskChanged , - Task, completed, .
calculate.
:

App.tsx, .
List — .
import React from 'react';
import { useProvide } from 'lamp-luwak';
import { Todo } from '../services/Todo';
import { Task } from './Task';
export const List = () => {
const todo = useProvide(Todo);
const items = todo.store;
if (items.length === 0) return null;
return (
<ul>
{items.map(item => (
<Task task={item} key={item.store.id} />
))}
</ul>
)
};
Todo, useProvide, List, , , . , .
Task — .
import React, { FC } from 'react';
import { useSubscribe } from 'lamp-luwak';
import { Task as TaskClass } from '../services/Todo/Task';
export const Task: FC<{ task: TaskClass }> = ({ task }) => {
useSubscribe(task);
const { label, completed } = task.store;
return (
<li>
<input
className="toggle"
type="checkbox"
checked={completed}
onChange={() => task.toggle()}
/>
<span style={{
textDecoration: completed ? 'line-through' : 'none'
}}>
{label}
</span>
</li>
)
};
Task, , useSubscribe, . , toggle .
Counters — .
import React from 'react';
import { useProvide } from 'lamp-luwak';
import { TodoCounters } from '../services/TodoCounters';
export const Counters = () => {
const { active, completed } = useProvide(TodoCounters).store;
return (
<>
<div>Active: {active}</div>
<div>Completed: {completed}</div>
</>
)
};
, useProvide . , TodoCounters.
Input — .
add Todo. , Todo, React useState.
import React, { useState } from 'react';
import { useProvide } from 'lamp-luwak';
import { Todo } from '../services/Todo';
export const Input = () => {
const [text, setText] = useState('Cook the lunch');
const todo = useProvide(Todo);
const add = () => {
todo.add(text);
setText('');
};
return (
<>
<input
onChange={(e) => setText(e.target.value)}
value={text}
autoFocus
onKeyDown={(event: any) => {
if (event.keyCode === 13) add();
}}
/>
<button onClick={add}>Add</button>
</>
);
};
App.tsx, src , .
import React from 'react';
import { Input } from './components/Input';
import { List } from './components/List';
import { Counters } from './components/Counters';
const App = () => (
<>
<Input />
<List />
<Counters />
</>
);
export default App;
Enjoy! .
lamp-luwak, , . .
, .
, , , .
.