Dactylographier et réagir

Le développement de Javascript devient parfois comme un détective. Comment comprendre le code de quelqu'un d'autre? C'est bien si le développeur a la compétence subtile de nommer les variables pour que les autres comprennent le point. Mais que se passe-t-il si les membres de l'équipe ne sont toujours pas toujours en mesure de comprendre l'intention de leur collègue? Comment comprendre ce qui entre dans l'argument d'une fonction?


Supposons que l'argument de la fonction s'appelle des erreurs. Probablement dans les erreurs est un tableau. Lignes les plus probables? Eh bien, ce tableau est compréhensible. Après tout, sa longueur est encore vérifiée. Mais la propriété length a également une chaîne. Il semble que pour le comprendre exactement, vous devez mettre un point d'arrêt et exécuter le script. Ensuite, parcourez complètement le script sur l'interface utilisateur (par exemple, nous avons besoin de la dernière étape du formulaire). Maintenant, dans devtools, on peut voir que les erreurs sont un objet avec un ensemble de champs spécifiques, y compris le champ de longueur.


Une telle ambiguïté dans l'analyse du code javascript entraîne une perte de temps pour les développeurs. Une bonne solution dans ce cas pourrait être dactylographiée (ci-après ts). Vous pouvez l'utiliser dans le prochain projet, ou mieux encore, faire son support dans le projet existant. Après cela, le temps pour comprendre le code de quelqu'un d'autre sera considérablement réduit. En effet, pour comprendre la structure de toute donnée, un clic suffit. Vous pouvez vous concentrer sur la logique de travail avec les données et savoir à tout moment que vous comprenez clairement le code.


Il convient de noter certains avantages de ts. Il est largement utilisé dans divers cadres et est étroitement lié au javascript. Le développement de ts est déterminé par les besoins des développeurs frontend.


Cet article présente le développement d'applications todo, mais seulement une brève description des points intéressants. Le code complet peut être trouvé ici .


J'ai utilisé react, tapuscript et mobx. Mobx est un outil flexible pour gérer l'état des applications. Mobx est concis. Il vous permet de travailler avec l'état des composants React dans un style synchrone. Pas de problème comme:


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

Dans ce cas, l'ancien state.name s'affiche.


De plus, mobx est pratique et n'interfère pas avec le travail avec les types ts. Vous pouvez décrire l'état en tant que classes distinctes ou directement à l'intérieur du composant react.


Par souci de simplicité, tous les composants sont placés dans le dossier des composants. Une classe est définie dans le dossier du composant avec une description de l'état qui est logiquement associé à l'affichage et au fonctionnement du composant.


Le dossier TodoItem contient un fichier avec le composant React TodoItem.tsx, un fichier avec les styles TodoItem.module.scss et un fichier d'état TodoItemState.ts.


TodoItemState.ts décrit les champs de stockage des données, comment y accéder et les règles pour les modifier. La gamme de possibilités est très large grâce à OOP et ts. Une partie des données peut être privée, une partie est en lecture seule, etc. À l'aide du décorateur @o, des champs observables sont spécifiés. Les composants React réagissent à leurs changements. Les décorateurs @a ( action ) sont utilisés dans les méthodes pour changer d'état.


 // 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; }; } 

Dans TodoItem.tsx, seules deux propriétés sont transmises aux accessoires. Dans mobx, il est optimal pour les performances globales d'une application de transférer des structures de données complexes vers le composant de réaction des accessoires. Puisque nous utilisons ts, nous pouvons indiquer précisément le type d'objet accepté par le composant.


 // 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> ); } } 

L'interface ITodoItemProps décrit la propriété todo de type TodoItemState. Ainsi, à l'intérieur du composant React, nous fournissons des données à afficher et des méthodes pour les modifier. De plus, les restrictions sur la modification des données peuvent être décrites à la fois dans la classe d'état et dans les méthodes du composant react, en fonction des tâches.


Le composant TodoList est similaire à TodoItem. Dans TodoListState.ts, vous pouvez voir les getters avec le décorateur @c (@computed). Ce sont des getters de classe ordinaires, seules leurs valeurs sont mémorisées et recomptées lorsque leurs dépendances changent. Calculé par conception est similaire aux sélecteurs redux. De manière pratique, il n'est pas nécessaire, comme React.memo ou reselect, de passer explicitement une liste de dépendances. Les composants React répondent aux changements calculés ainsi qu'aux changements observables. Une caractéristique intéressante est que le recalcul de la valeur ne se produit pas si le calcul n'est pas actuellement impliqué dans le rendu (ce qui économise des ressources). Par conséquent, malgré le maintien de valeurs de dépendance constantes, le calcul peut être recalculé (il existe un moyen de dire explicitement à mobx de sauvegarder la valeur calculée).


 // 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; }; } 

L'accès à la liste des tâches est ouvert uniquement via un champ calculé, où, en fonction du mode d'affichage, l'ensemble de données filtré nécessaire est renvoyé (terminé, actif ou toutes les tâches). Les dépendances de tâches spécifient les champs completeTodos, activeTodos et _todos observables privés calculés.


Considérez le composant principal de l'application. Il affiche un formulaire pour ajouter de nouvelles tâches et une liste de tâches. Une instance de l'état AppSate principal est instantanément créée.


 // 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> ); } } 

Le champ appState contient une instance de la classe TodoListState pour afficher le composant TodoList et la méthode pour ajouter de nouvelles tâches, qui est passée au composant 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); }; } 

Le composant AddTodo a un état isolé. Il n'y a pas accès depuis l'état général. La seule connexion à appState se fait via la méthode appState.addTodo lors de la soumission d'un formulaire.
Pour l'état du composant AddTodo, la bibliothèque formstate est utilisée, ce qui est de grands amis avec ts et mobx. Formstate vous permet de travailler facilement avec des formulaires, de valider des formulaires, etc. Le formulaire n'a qu'un seul nom de champ obligatoire.


 // 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; }; } 

En général, cela n'a aucun sens de décrire complètement le comportement de tous les composants. Le code complet est donné ici .


Cet article décrit la tentative de l'auteur d'écrire du code simple, flexible et structuré facile à gérer. React divise l'interface utilisateur en composants. Les composants décrivent les classes d'état (chaque classe peut être testée séparément). Les instances d'états sont créées soit dans le composant lui-même, soit à un niveau supérieur, selon les tâches. Assez commodément, vous pouvez spécifier les types de champ de classe et les types de propriété de composant grâce à typescript. Grâce à mobx, nous pouvons, presque imperceptiblement pour le développeur, faire réagir les composants aux changements de données.

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


All Articles