Gestion de l'état orientée service avec Lamp-Luwak

Étant donné que React offre des capacités incroyables pour travailler avec l'affichage, vous ne pouvez vous concentrer que sur l'organisation de la logique d'application et la sémantique du code qui décrit comment travailler avec les données. Ceux. en choisissant une bibliothèque de gestion d'état, le choix du style de la future base de code a lieu.


Dans cet article, nous considérerons une approche basée sur un magasin de services implémentée via une bibliothèque lamp-luwak.


image lampe-luwak


TL; DR Celui qui veut commencer à écrire du code bientôt peut passer au paragraphe suivant.


Toute la logique à l'intérieur de votre application est divisée en services, chaque service contient un seul côté, dont le changement informe les abonnés. Le service contient également des fonctions logiques, où vous pouvez accéder à d'autres services et réécrire le côté immuable, ces fonctions peuvent être asynchrones et contenir des effets secondaires.


lamp-luwak , . , React useProvide useSubscribe . , , .


, - . , embedded . .


subscribe. .



, todo- :


  • ;
  • / ;
  • /.


todos create-react-app


npx create-react-app todos --template typescript --use-npm
# or
yarn create react-app todos --template typescript

lamp-luwak todos


npm i --save lamp-luwak
# or
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, -, - .


// Todo.ts
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.


// Todo/Task.ts
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, .


// TodoCounters.ts
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.


:


image d'architecture de service



App.tsx, .


List — .


// List.tsx
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 — .


// Task.tsx
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 — .


// Counters.tsx
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.


// Input.tsx
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 , .


// App.tsx
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, , . .



, .



, , , .
.




All Articles