Лучшие практики кодирования на React и Redux

Лучшие практики кодирования на React и Redux

От автора: этим летом я закончил стажировку в Scott Logic, в их офисе в Ньюкасле. Имея небольшой опыт реализации реальных программных проектов, я обнаружил, что много смог почерпнуть из этого опыта. Мы работали над киоском для honesty.store. honesty.store — это магазин офисных товаров, он основан на идее, что люди платят за товары на веб-сайте и просто забирают их. Киоск позволял пользователям отправлять самим себе напоминания в Slack для оплаты товара.

Для этого мы использовали React / Redux для UI, TensorFlow для создания модели машинного обучения идентификации товаров и облачные сервисы, предоставляемые Firebase от Google для хостинга, облачных функций и хранения. В этом посте я расскажу, какой может быть практика React и Redux.

Лучшие советы

React, как вы, наверное, знаете, это библиотека javascript. Она эффективна, она упрощает создание сложных пользовательских интерфейсов с помощью небольших блоков кода, называемых компонентами, она даже может выгулять вашу собаку (одного из вышеперечисленного она не может). Redux может использоваться с React для обмена информацией о состоянии между компонентами, не передавая его компонентам, которые в состоянии не нуждаются.

Функциональные компоненты

Сначала мы сравним две разные базы для создания компонентов — это, чтобы ваш код был как можно более простым. И чтобы он не делал то, чего вы не хотели бы, это похоже на использование const вместо let.

Давайте создадим компонент ShoppingList. Этот компонент должен принимать массив в качестве свойства следующим образом: <ShoppingList items={[«Wand», «Robes», «Owl», «Bertie Bott’s Every Flavour Beans»]} />, — и отображать его как список. Мы можем сделать это через компонент React, например:

class ShoppingList extends Component { render() { return ( <div className="shopping-list"> <ul> {this.props.items.map(item => (<li>{item}</li>))} </ul> </div> ); }
}

Или мы можем создать такой функциональный компонент без состояния:

const ShoppingList = ({ items }) => ( <div className="shopping-list"> <ul> {items.map(item => (<li>{item}</li>))} </ul> </div> )

Оба они дают одинаковый результат, так который из них лучше? Функциональный компонент не может иметь никакого состояния или использовать методы жизненного цикла, что делает код более простым, поскольку читатель мгновенно сможет сказать, что функция не может сделать. Вы можете использовать раздутый компонент, если хотите использовать метод жизненного цикла, например componentWillUnmount. Это метод жизненного цикла, который вызывается непосредственно перед размонтированием компонента (когда он больше не отображается). Это должно использоваться для очистки, например, для удаления задержек и интервалов.

Избегайте ненужной информации в Redux

Этот совет связан с минимизацией информации, хранящейся в Redux, что упрощает ее чтение и обновление. Предположим, мы хотим хранить информацию о пользователе в Redux, мы могли бы хранить ее так:

{ "age": 15, "isAdult": false
}

Однако второе свойство полностью избыточно. Это может означать, что вам не нужно выполнять проверку state.age> 17 после получения возраста, но это также означает, что вам нужно выполнять эту проверку каждый раз, когда вы устанавливаете возраст. Это также вызовет раздутие кода при расширении этой системы, например, если вы хотите проверить, является ли пользователь ребенком и / или подростком.

Давайте возьмем еще один пример: скажем, мы хотим хранить информацию о нескольких домашних животных, доступных для покупки, и выбрать одно из них:

{ "pets": { "Hedwig": {"type": "owl", "gender": "female"}, "Scabbers": {"type": "Rat", "gender": "male"}, "Trevor": {"type": "Toad", "gender": "male"}, "Crookshanks": {"type": "Cat", "gender": "male"} }, "selectedPet": {"name": "Hedwig", "type": "owl", "gender": "female"}
}

Здесь вместо хранения объекта в «selectedPet» мы можем хранить такой идентификатор:

{ "pets": { "Hedwig": {"type": "owl", "gender": "female"}, "Scabbers": {"type": "Rat", "gender": "male"}, "Trevor": {"type": "Toad", "gender": "male"}, "Crookshanks": {"type": "Cat", "gender": "male"} }, "selectedPet": "Hedwig"
}

Если мы хотим получить всю информацию о выбранном домашнем животном, мы можем использовать state.pets[state.selectedPet].

Как это связано с поведением ваших компонентов? Предположим, у нас есть компонент Checkout, который нуждается в информации о выбранном домашнем животном. Возможно, у вас возникнет соблазн передать в state.selectedPet и компонент state.pets, например:

import Checkout from './Checkout';
import {connect} from 'react-redux'; const mapStateToProps = state => ({ pets: state.pets, selectedPet: state.selectedPet
}); export default connect( mapStateToProps,
)(Checkout);

Но здесь вы передаете компоненту слишком много информации, а это значит, что код должен быть более сложным, а тот, кто читает его, не глупым. Немые компоненты полезны, поскольку они, как правило, более многоразовые. Теперь посмотрим на следующий код:

import Checkout from './Checkout';
import {connect} from 'react-redux'; const getSelectedPet = state => ({ ...state.pets[state.selectedPet], name: state.selectedPet
}); const mapStateToProps = state => ({ pet: getSelectedPet(state)
}); export default connect(mapStateToProps)(Checkout);

Где мы использовали обозначение распространения. Это означает, что свойство никогда не видит информации, в которой не нуждается.

Передача функций в качестве свойства

Наконец, простое изменение, которое вы можете сделать, чтобы повысить эффективность вашего кода. Давайте создадим компонент School, в котором мы можем добавлять учеников одним нажатием кнопки. Мы могли бы использовать этот код:

class School extends Component { state = {students: []}; addStudent() { //get student info //add student to state } render() { return ( <div> <p>Students: {this.state.students}</p> <button onClick={() => this.addStudent()}>Add Student</button> </div> ); }
}

Как вы уже знаете, onClick={() => this.addStudent()} не очень эффективно, потому что это создает новую функцию. В React все может быть еще хуже, потому что новая функция будет создаваться для каждого рендеринга. Теперь давайте попробуем следующее:

class School extends Component { state = {students: []}; addStudent = () => { //get student info //add student to state }; render() { return ( <div> <p>Students: {this.state.students}</p> <button onClick={this.addStudent()}>Add Student</button> </div> ); }
}

Это намного лучше. Обратите внимание на изменение в определении напоминания об изменении. Это связано с природой ключевого слова this. Используя функцию стрелки в одном из двух мест, мы обеспечиваем принадлежность к экземпляру компонента School, который мы нажали. Что означает this, зависит от того, как была вызвана функция.

Бонусный совет — используйте инструменты для разработчиков

Инструменты для разработчиков для React и Redux очень полезны. React дает вам информацию о том, какие компоненты были обработаны, их состояние и свойства. Redux позволяет увидеть историю действий с полным состоянием и действием на каждом этапе. Вы также можете изменить состояние компонентов и хранилище Redux.

Заключение

React и Redux очень полезны, но не волшебны. Я начал этот проект без каких-либо знаний о React, и эти советы — это менее очевидные фрагменты информации, которые я узнал. Они не являются критическими для создания рабочего приложения, их можно легко пропустить, но они очень полезны, если вы хотите сделать свой код более удобным для обработки и более чистым для чтения.

Автор: Gurveer Arora

Источник: https://blog.scottlogic.com/

Редакция: Команда webformyself.