打字稿和反应

JavaScript开发有时变得像侦探。 如何理解别人的代码? 如果开发人员具有微妙的命名变量技巧,以便其他人理解这一点,那将是很好的。 但是,如果团队成员仍然不能始终了解同事的意图该怎么办? 如何理解函数的参数?


假设函数参数称为错误。 可能在错误中是一个数组。 最可能的线? 好吧,这个数组是可以理解的。 毕竟,它的长度还要进一步检查。 但是length属性也有一个字符串。 为了准确地找出它,您似乎需要放置一个断点并运行脚本。 然后,完全通过UI上的脚本(例如,我们需要表单的最后一步)。 现在在devtools中可以看到,错误是一个具有一组特定字段(包括长度字段)的对象。


这种在解析javascript代码方面的歧义性导致浪费开发人员时间。 在这种情况下,一个好的解决方案是打字稿(以下简称ts)。 您可以在下一个项目中使用它,或者甚至更好地在现有项目中提供支持。 之后,了解他人代码的时间将大大减少。 确实,为了了解任何数据的结构,单击一下就足够了。 您可以专注于处理数据的逻辑,并且随时知道自己清楚地了解代码。


应该注意ts的一些优点。 它广泛用于各种框架中,并且与javascript密切相关。 ts的开发取决于前端开发人员的需求。


本文介绍了todo应用程序的开发,但仅简要介绍了一些有趣的方面。 完整的代码可以在这里找到。


我用了React,TypeScript和Mobx。 Mobx是用于管理应用程序状态的灵活工具。 Mobx简洁明了。 它允许您以同步方式处理React组件的状态。 像这样没有问题:


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

在这种情况下,将显示旧的state.name。


另外,mobx方便且不会干扰使用ts类型的工作。 您可以将状态描述为单独的类或直接在react组件内部进行描述。


为简单起见,所有组件都放置在components文件夹中。 在组件的文件夹中定义了一个类,其中描述了与组件的显示和操作在逻辑上相关的状态。


TodoItem文件夹包含一个带有react组件TodoItem.tsx的文件,一个具有TodoItem.module.scss样式的文件以及一个TodoItemState.ts状态文件。


TodoItemState.ts描述了用于存储数据的字段,如何访问它们以及更改它们的规则。 由于OOP和ts,可能性范围非常大。 部分数据可以是私有的,部分数据是只读的,依此类推。 使用@o装饰器,指定可观察的字段。 React组件会对其更改做出反应。 @a( 动作 )修饰符用于更改状态的方法中。


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

在TodoItem.tsx中,仅两个属性传递给prop。 在mobx中,对于应用程序的整体性能而言,将复杂的数据结构传输到props react组件是最佳的。 由于使用ts,因此可以精确地指示组件所接受的对象的类型。


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

ITodoItemProps接口描述了TodoItemState类型的todo属性。 因此,在react组件内部,我们提供了用于显示的数据和更改它们的方法。 此外,根据任务,可以在状态类和React组件的方法中描述更改数据的限制。


TodoList组件类似于TodoItem。 在TodoListState.ts中,您可以看到带有@c(@computed)修饰符的吸气剂。 这些是普通的类获取器,只有它们的值被存储并在其依赖项更改时重新计数。 设计计算类似于redux选择器。 方便地,没有必要像React.memo或reselect那样显式传递依赖项列表。 React组件对计算出的变化以及可观察到的变化做出响应。 一个有趣的功能是,如果当前未在渲染中涉及计算值,则不会重新计算该值(这样可以节省资源)。 因此,尽管保持恒定的相关性值,也可以重新计算出计算出的值(有一种方法可以明确告诉mobx保存计算出的值)。


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

访问待办事项列表仅通过计算字段打开,在该字段中,根据查看模式,将返回必要的已过滤数据集(已完成,活动或所有待办事项)。 待办事项依赖项指定计算出的completedTodos,activeTodos和私有可观察的_todos字段。


考虑应用程序的主要组成部分。 它呈现了一个用于添加新待办事项和待办事项列表的表单。 立即创建主AppSate状态的实例。


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

appState字段包含TodoListState类的实例以显示TodoList组件以及添加新todo的方法,该方法将传递给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); }; } 

AddTodo组件具有隔离状态。 无法从一般状态访问它。 提交表单时,与appState的唯一连接是通过appState.addTodo方法。
对于addTodo组件的状态,使用了formstate库,它是ts和mobx的好朋友。 Formstate允许您方便地使用表单,验证表单等。 该表单只有一个必填字段名称。


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

通常,完全描述所有组件的行为是没有意义的。 完整的代码在这里给出。


本文介绍了作者尝试编写易于维护的简单,灵活和结构化的代码。 React将UI分为多个组件。 组件描述状态类(每个类可以单独测试)。 状态实例可以在组件本身中创建,也可以在更高级别上创建,具体取决于任务。 足够方便的是,借助typescript,您可以指定类字段类型和组件属性类型。 感谢mobx,对于开发人员而言,我们几乎可以无察觉地使React组件对数据更改做出反应。

Source: https://habr.com/ru/post/zh-CN462597/


All Articles