Datilografe e reaja

O desenvolvimento de Javascript às vezes se torna um detetive. Como entender o código de outra pessoa? É bom que o desenvolvedor tenha a habilidade sutil de nomear variáveis ​​para que outras pessoas entendam o ponto. Mas e se os membros da equipe nem sempre conseguirem entender a intenção do colega? Como entender o que entra no argumento de uma função?


Suponha que o argumento da função seja chamado de erros. Provavelmente nos erros há uma matriz. Linhas mais prováveis? Bem, essa matriz é compreensível. Afinal, seu comprimento é verificado ainda mais. Mas a propriedade length também tem uma string. Parece que, para descobrir exatamente, você precisa colocar um ponto de interrupção e executar o script. Depois, percorra completamente o script na interface do usuário (por exemplo, precisamos da etapa final do formulário). Agora, no devtools, pode-se ver que error é um objeto com um conjunto de campos específicos, incluindo o campo length.


Essa ambiguidade na análise do código javascript leva a uma perda de tempo do desenvolvedor. Uma boa solução nesse caso pode ser datilografada (a seguir ts). Você pode usá-lo no próximo projeto, ou melhor ainda, fazer seu suporte no projeto existente. Depois disso, o tempo para entender o código de outra pessoa será reduzido significativamente. De fato, para entender a estrutura de qualquer dado, basta um clique. Você pode se concentrar na lógica de trabalhar com dados e a qualquer momento saber que entende claramente o código.


Algumas vantagens de ts devem ser observadas. É amplamente utilizado em várias estruturas e está intimamente relacionado ao javascript. O desenvolvimento do ts é determinado pelas necessidades dos desenvolvedores de front-end.


Este artigo apresenta o desenvolvimento de aplicativos de tarefas, mas apenas uma breve descrição de pontos interessantes. O código completo pode ser encontrado aqui .


Eu costumava reagir, datilografado e mobx. Mobx é uma ferramenta flexível para gerenciar o estado do aplicativo. Mobx é conciso. Ele permite que você trabalhe com o estado dos componentes de reação em um estilo síncrono. Nenhum problema como:


this.setState({name: 'another string'}); alert(this.state.name); 

Nesse caso, o antigo state.name é exibido.


Além disso, o mobx é conveniente e não interfere no trabalho com os tipos ts. Você pode descrever o estado como classes separadas ou diretamente dentro do componente de reação.


Para simplificar, todos os componentes são colocados na pasta de componentes. Uma classe é definida na pasta do componente com uma descrição do estado que está logicamente associado à exibição e operação do componente.


A pasta TodoItem contém um arquivo com o componente de reação TodoItem.tsx, um arquivo com os estilos TodoItem.module.scss e um arquivo de status TodoItemState.ts.


TodoItemState.ts descreve os campos para armazenar dados, como acessá-los e as regras para alterá-los. O leque de possibilidades é muito grande, graças ao OOP e ts. Parte dos dados pode ser particular, parte é somente leitura e assim por diante. Usando o decorador @o, os campos observáveis ​​são especificados. Componentes de reação reagem às suas alterações. Os decoradores @a ( action ) são usados ​​em métodos para alterar o estado.


 // TodoItemState.ts import { action as a, observable as o } from 'mobx'; export interface ITodoItem { id: string; name: string; completed: boolean; } export class TodoItemState { @o public readonly value: ITodoItem; @o public isEditMode: boolean = false; constructor(value: ITodoItem) { this.value = value; } @a public setIsEditMode = (value: boolean = true) => { this.isEditMode = value; }; @a public editName = (name: string) => { this.value.name = name; }; @a public editCompleted = (completed: boolean) => { this.value.completed = completed; }; } 

Em TodoItem.tsx, apenas duas propriedades são passadas para props. No mobx, é ideal para o desempenho geral de um aplicativo transferir estruturas de dados complexas para o componente de reação dos adereços. Como usamos ts, podemos indicar com precisão o tipo de objeto aceito pelo componente.


 // TodoItem.tsx import React, { ChangeEventHandler } from 'react'; import { observer } from 'mobx-react'; import { TodoItemState } from './TodoItemState'; import { EditModal } from 'components/EditModal'; import classNames from 'classnames'; import classes from './TodoItem.module.scss'; export interface ITodoItemProps { todo: TodoItemState; onDelete: (id: string) => void; } @observer export class TodoItem extends React.Component<ITodoItemProps> { private handleCompletedChange: ChangeEventHandler<HTMLInputElement> = e => { const { todo: { editCompleted }, } = this.props; editCompleted(e.target.checked); }; private handleDelete = () => { const { onDelete, todo } = this.props; onDelete(todo.value.id); }; private get editModal() { const { todo } = this.props; if (!todo.isEditMode) return null; return ( <EditModal name={todo.value.name} onSubmit={this.handleSubmitEditName} onClose={this.closeEditModal} /> ); } private handleSubmitEditName = (name: string) => { const { todo } = this.props; todo.editName(name); this.closeEditModal(); }; private closeEditModal = () => { const { todo } = this.props; todo.setIsEditMode(false); }; private openEditModal = () => { const { todo } = this.props; todo.setIsEditMode(); }; render() { const { todo } = this.props; const { name, completed } = todo.value; return ( <div className={classes.root}> <input className={classes.chackbox} type="checkbox" checked={completed} onChange={this.handleCompletedChange} /> <div onClick={this.openEditModal} className={classNames( classes.name, completed && classes.completedName )}> {name} </div> <button onClick={this.handleDelete}>del</button> {this.editModal} </div> ); } } 

A interface ITodoItemProps descreve a propriedade todo do tipo TodoItemState. Assim, dentro do componente de reação, somos fornecidos com dados para exibição e métodos para alterá-los. Além disso, as restrições à alteração de dados podem ser descritas tanto na classe de estado quanto nos métodos do componente de reação, dependendo das tarefas.


O componente TodoList é semelhante ao TodoItem. Em TodoListState.ts, você pode ver getters com o decorador @c (@computed). Estes são getters de classe comuns, apenas seus valores são memorizados e recontados quando suas dependências mudam. Computado pelo design é semelhante aos seletores de redux. Convenientemente, não é necessário, como React.memo ou selecionar novamente, passar explicitamente uma lista de dependências. Os componentes de reação respondem a alterações computadas, bem como a alterações observáveis. Uma característica interessante é que o recálculo do valor não ocorrerá se o cálculo não estiver atualmente envolvido na renderização (o que economiza recursos). Portanto, apesar de manter valores de dependência constantes, o cálculo pode ser recalculado (existe uma maneira de dizer explicitamente ao mobx para salvar o valor calculado).


 // TodoListState.ts import { action as a, observable as o, computed as c } from 'mobx'; import { ITodoItem, TodoItemState } from 'components/TodoItem'; export enum TCurrentView { completed, active, all, } export class TodoListState { @o public currentView: TCurrentView = TCurrentView.all; @o private _todos: TodoItemState[] = []; @c public get todos(): TodoItemState[] { switch (this.currentView) { case TCurrentView.active: return this.activeTodos; case TCurrentView.completed: return this.completedTodos; default: return this._todos; } } @c public get completedTodos() { return this._todos.filter(t => t.value.completed); } @c public get activeTodos() { return this._todos.filter(t => !t.value.completed); } @a public setTodos(todos: ITodoItem[]) { this._todos = todos.map(t => new TodoItemState(t)); } @a public addTodo = (todo: ITodoItem) => { this._todos.push(new TodoItemState(todo)); }; @a public removeTodo = (id: string): boolean => { const index = this._todos.findIndex(todo => todo.value.id === id); if (index === -1) return false; this._todos.splice(index, 1); return true; }; } 

A lista de tarefas de acesso é aberta apenas através de um campo calculado, onde, dependendo do modo de visualização, o conjunto de dados filtrados necessário é retornado (concluído, ativo ou todo o todo). As dependências de todo especificam os campos completeTodos, activeTodos e _todos observáveis ​​privados calculados.


Considere o componente principal do aplicativo. Ele renderiza um formulário para adicionar novas tarefas e uma lista de tarefas. Uma instância do estado principal do AppSate é criada instantaneamente.


 // App.tsx import React from 'react'; import { observer } from 'mobx-react'; import { TodoList, initialTodos } from 'components/TodoList'; import { AddTodo } from 'components/AddTodo'; import { AppState } from './AppState'; import classes from './App.module.scss'; export interface IAppProps {} @observer export class App extends React.Component<IAppProps> { private appState = new AppState(); constructor(props: IAppProps) { super(props); this.appState.todoList.setTodos(initialTodos); } render() { const { addTodo, todoList } = this.appState; return ( <div className={classes.root}> <div className={classes.container}> <AddTodo onAdd={addTodo} /> <TodoList todoListState={todoList} /> </div> </div> ); } } 

O campo appState contém uma instância da classe TodoListState para exibir o componente TodoList e o método para adicionar novo todo, que é passado ao componente AddTodo.


 // AppState.ts import { action as a } from 'mobx'; import { TodoListState } from 'components/TodoList'; import { ITodoItem } from 'components/TodoItem'; export class AppState { public todoList = new TodoListState(); @a public addTodo = (value: string) => { const newTodo: ITodoItem = { id: Date.now().toString(), name: value, completed: false, }; this.todoList.addTodo(newTodo); }; } 

O componente AddTodo possui um estado isolado. Não há acesso a ele do estado geral. A única conexão ao appState é através do método appState.addTodo ao enviar um formulário.
Para o estado do componente AddTodo, é usada a biblioteca formstate, que é uma ótima amiga de ts e mobx. O Formstate permite que você trabalhe convenientemente com formulários, valide formulários e muito mais. O formulário possui apenas um nome de campo obrigatório.


 // AddTodoState.ts import { FormState, FieldState } from 'formstate'; export class AddTodoState { // Create a field public name = new FieldState('').validators( val => !val && 'name is required' ); // Compose fields into a form public form = new FormState({ name: this.name, }); public onSubmit = async () => { // Validate all fields const res = await this.form.validate(); // If any errors you would know if (res.hasError) { console.error(this.form.error); return; } const name = this.name.$; this.form.reset(); return name; }; } 

Em geral, não faz sentido descrever completamente o comportamento de todos os componentes. O código completo é fornecido aqui .


Este artigo descreve a tentativa do autor de escrever código simples, flexível e estruturado, fácil de manter. React divide a interface do usuário em componentes. Os componentes descrevem classes de estado (cada classe pode ser testada separadamente). Instâncias de estados são criadas no próprio componente ou em um nível superior, dependendo das tarefas. Convenientemente, você pode especificar tipos de campos de classe e tipos de propriedades de componentes graças ao texto datilografado. Graças ao mobx, podemos, quase imperceptivelmente para o desenvolvedor, fazer com que os componentes de reação reajam às alterações de dados.

Source: https://habr.com/ru/post/pt462597/


All Articles