Using class fields in React (with create-react-app and Babel)
Jun 26, 2018 · 5 min read
React is one of the most popular Javascript libraries in the market. It claims to be “simple” and, once you are familiar with, it’s really simple. But, at the beginning, if you are a newbie trying to learn the basics, the first issue is to setup properly your development environment. Too many things involved: jsx, node, npm, webpack, babel, ES6+, etc. People at Facebook is aware of this complexity and developed a solution: create-react-app.
With create-react-app you only need one line command to setup your project:
(npx comes with npm 5.2+ and higher)
And now you have a brand new app React app that includes:
It sounds great!! But, what is happening under the hood? All this features just works automagically, and that’s great to save time when you just want to start coding. But sometimes you need (or maybe just want) to understand how everything is working behind the scenes.
In this article, we are going to explore one of these features: Class instance properties
Setting initial state
In a standard React app, you define your initial state in this way:
You should be familiar with this syntax. We have a constructor, inside the constructor is the only place where you can access the state directly, using this.state. Outside the constructor, you should use the setState() method.
But, if you are using create-react-app you are allowed to use this alternative syntax:
Bounded methods
You should also be familiar with the syntax to bind the context (this) to your methods, using bind() inside the constructor.
If you forget to bind the this, you’ll get the following error: TypeError: Cannot read property ‘setState’ of undefined. Sounds familiar?
Enters create-react-app
But, if you created your app using create-react-app, you are allowed to do this:
There are some things that looks strange, like the state initialization:
This class doesn’t have a constructor, and outside the constructor we should use this.setState, but here we are accessing state directly, and we don’t need to use “this” at all!
Well, this is a class field initializer. Using initializers, this code:
Is the equivalent to:
If you try it in the Babel repl, you can see they produce the same result:
(For a detailed explanaition, look here)
This is not part of the current JS specification, it’s a stage 3 proposal, you can check it here. But you can have it with Babel, using the babel-plugin-transform-class-properties plugin.
To use the babel-plugin-transform-class-properties plugin without create-react-app, you should need to install it:
Then, why is it working? Because create-react-app includes this configuration in Babel out-of-the-box. See: Supported Language Features and Polyfills.
And there is another strange piece of syntax.
Inside a ES6 class, we declare methods in this way:
The previous example is a class method, it’s the equivalent of of using the function keyword, but with ES6 syntactic sugar. The same as regular functions, the value of this depends on how the function was called. When we use this syntax, is necessary to bind the this.
But, in our create-react-app application, we have this syntax:
Well, this is an alternative syntax, using class fields and an arrow function (remember, this is already setup for you in create-react-app). As long as arrow functions binds the “this” to the context in which it was created, there is no need to bind it manually.
In other words, this:
Is exactly the same as this:
Conclusion: Tools like create-react-app are great, they allow you to concentrate in the coding tasks without the need to spend a lot of your time working in the configuration. But it has a downside, your project becomes some kind of black box, with a lot things happening behind the scenes and maybe you are not able to notice or understand the “hows” and “whys”. Dissecting your black box is a great way to learn some advanced topics in modern development and I strongly encourage you to do it. For me, writing this article was a great opportunity to deep dive into some things that were “just working” but I was not able to thoroughly understand.
Public Class Fields with React Components
Share this video with your friends
Public Class Fields allow you to add instance properties to the class definition with the assignment operator ( = ). In this lesson, we’ll look at their use case for simplifying event callbacks and state initialization with a React component.
Course
JavaScript from Kent
[00:00] Here we have a basic react component called «App.» In its constructor, it is initializing some state. In the render method, we’re rendering out the Click Count. That’s the current state of the clicks, as well as a button called «Click Me!» that passes the handleClick handler to the onClick callback.
[00:19] Here in handleClick where you setState and we increment clicks. Here there’s a problem if we go ahead and click, then we’re going to see an error «this.setState is not a function.» That’s because we’re passing handleClick to this onClick callback.
[00:35] When react is calling our our onClick callback with the event, it’s not calling it with the right context. This is not actually bound to anything at all. That’s why we’re getting this «this.setState is not a function.»
[00:45] This is pretty easy to solve. Most people will just make this an arrow function. Then you’ll have to invoke this. Now if we click, we’ll see that the click count is incrementing.
[00:55] There are a couple of drawbacks to this. First of, if you want to pass on the event, now you have to type «event» here and «event» here so that you can get access to the event. Another issue there is if there are multiple parameters, then you have to say «param2,» then you’ll have to go here and «param2.»
[01:16] It won’t be long before you start just saying, «The heck with it.» We’ll say «args» and «. args» so that you don’t have to keep in adding parameters, or whatever. But this is not really ergonomic. I don’t really like doing things this way.
[01:32] Another way to accomplish this same kind of idea is instead of using arrow function, which doesn’t have at this binding and simply uses this binding of its closure, we’ll say «.bind this,» so we specify that this binding for the function explicitly. This works just as well and you don’t need to worry about parameters or any arguments or anything.
[01:57] That’s great, but I still don’t really like having to do the binding here. It also suffers from the performance issues with the arrow function where you’re creating a new function every single time the render method is called, which is actually a lot of times often in react components.
[02:13] Instead of doing the binding here, we can actually do the binding in our constructor. We can say «this.handleClick = this.handleClick.bind this.» What this does effectively is it overwrites these instances of handleClick with a bound version of handleClick so that you can pass it on.
[02:34] It doesn’t matter how it’s being called. It’s always going to be bound with this, with our instance of the component. That works great.
[02:42] Let’s say that you have many handleClick or of handle key up or handle mouse over, you have a bunch of these in your component. Now you have to have not only the definition, but also a line in your constructor binding that definition. That’s also not super ergonomic.
[03:01] With public class fields, you can actually solve this problem by basically taking this assignment looking operator and sticking it right here in the class definition. Then we’ll need to turn this into a function, and then we want to have this function be bound to this. Then we’ll comment this out.
[03:19] Because we have this assignment syntax here, the public class field specification basically says, «Postpone evaluating this expression until each instance is being constructed.» We’re going to add a handleClick property on each instance and assign it to this expression.
[03:39] This runs effectively. It runs in the controller effectively. You’re doing the same type of thing as this would do as we had before.
[03:47] Now we can click and that works just fine. We can make this even a little bit more ergonomic by removing the binding here and just going with lexical binding for this using an arrow function. Now, we can click and increment and everything works nicely with public class fields.
[04:05] We can do the same things with state. We’ll just copy and paste the state assignment and put it as part of the class definition. Now that state assignment will take place when the instance is being constructed as well.
[04:16] We’ll get rid of this, have that commented out. You’ll see that the click count is still initialized to zero. If we initialize it to six, then that works just fine.
[04:26] Now you can see that the constructor is totally useless. It’s not really doing anything at all. We can get rid of that entirely and have our App component still function exactly as we want it to.
[04:39] That’s public class fields. It’s simply the name of the property, and then the «equals» operator. Then the expression that we want to have it initialize to when each instance is being initialized.
React class components with ES6 and Class fields.
With the advent of ES6 and Class fields we can now write shorter components and without the need of worrying with the this keyword bind.
Note: This is a follow up article based on youtube screencast I made. If you prefer watching, feel free to watch my screencast. https://youtu.be/pTREFnFCh5E
ES6 introduced an array of cool things to the JavaScript language, check out this great web site, which goes over each one of the new features.
Class fields as of mid 2018 is a TC39 proposal currently on stage 3, I strongly believe it will make it to stage 4 due to its wide spread adoption in the React community.
Class fields enables us to write property values outside of the constructor in classes, you no longer will need to instantiate these values inside the constructor, for example:
Class component with constructor.
Class component using class fields.
Looking at both code snippets above, the first with constructor, the second without it. With class fields we no longer need the constructor since the class field will instantiate the property value to the classe’s instance.
If we leverage ES6 arrow functions with the class fields, we no longer will need to worry about this binding, since the this scope inside an arrow function points to the parent scope.
Using ES6 Object Destructuring to access values in objects.
Make use of object destructuring so we don’t repeat ourselves when accessing values in this.props or this.state.
Without object destructuring.
With object destructuring.
Example above uses object destructuring assignment to retrieve title and description from this.props.
Spread operator
We can also use the spread operator to pass down an object as properties of a Component.
Case we had a Header component which received the title and description props I could spread the this.props into it. For example:
By spreading an object to the component as you can see above, the spread operator iterates through each key in the object and passes it down to the component. It works the same as writing title= and description=
But be careful, you don’t want to pass down property values which you won’t consume in your component.
Combine object destructuring with spread operator.
You can also combine both to get the remaining values inside an object, for example:
By using the spread operator inside the object destructuring you can retrieve the remaining properties.
Curried functions with computed property names.
We can also leverage ES6 computed property name together with curried functions to have a declarative way of updating state of inputs.
Take for example this form:
We define a method for each input to update state. Instead of this we can leverage curried functions to make this much shorter. For example:
Thanks to the computed property values:
we can write this in a much shorter way.
I love JavaScript’s growth mindset, instead of us giving up on it, it continues to try to improve its specs.
Thanks for reading, I’m Andrei Calazans a Software Developer in love with learning and teaching. Feel free to reach out and chat.
React.Component
Эта страница содержит подробный справочник API для определения класса компонента React. Предполагается, что вы знакомы с фундаментальными концепциями React, такими как компоненты и свойства, а также состояние и жизненный цикл. Если вы этого не сделали, сначала прочитайте их.
React позволяет вам определять компоненты как классы или функции. В настоящее время компоненты, определённые как классы, предоставляют больше возможнотей, которые подробно описаны на этой странице. Чтобы определить класс компонента React, вам необходимо расширить React.Component :
Мы настоятельно не рекомендуем создавать собственные классы базовых компонентов. В React-компонентах повторное использование кода в основном достигается с помощью композиции, а не наследования.
React не заставляет вас использовать синтаксис класса ES6. Если вы предпочитаете избегать его использования, вы можете установить пакет create-react-class или аналогичную пользовательскую абстракцию. Посмотрите на Использование React без ES6, чтобы узнать больше.
Жизненный цикл компонента
Каждый компонент имеет несколько «методов жизненного цикла», которые вы можете переопределить для выполнения кода в определенное время в процессе. Вы можете использовать эту диаграмму жизненного цикла в качестве шпаргалки. В приведённом ниже списке обычно используемые методы жизненного цикла выделены полужирным шрифтом. Остальные из них существуют для относительно редких случаев использования.
Эти методы вызывают в следующем порядке, когда экземпляр компонента создаётся и добавляется в DOM:
Эти методы считаются устаревшими, и вам следует избегать их использование в новом коде:
Обновление может быть вызвано изменениями в свойствах или состоянии. Эти методы вызываются в следующем порядке, когда компонент повторно отрисовывается:
Эти методы считаются устаревшими, и вам следует избегать их использование в новом коде:
Этот метод вызывается, когда компонент удаляется из DOM:
Этот метод вызывается при возникновении ошибки во время отрисовки, в методе жизненного цикла или в конструкторе любого дочернего компонента.
Каждый компонент также предоставляет некоторые другие методы API:
Общеиспользуемые методы жизненного цикла
Методы в этом разделе охватывают подавляющее большинство случаев использования, с которыми вы столкнётесь при создании React-компонентов. Для наглядной иллюстрации ознакомьтесь с этой диаграммой жизненного цикла.
Метод render() — единственный обязательный методов в классовом компоненте.
При вызове он должен проверять this.props и this.state и возвращать один из следующих типов:
Функция render() должна быть чистой, что означает, что она не изменяет состояние компонента, она возвращает один и тот же результат при каждом вызове и не взаимодействует напрямую с браузером.
Если вам нужно взаимодействовать с браузером, выполняйте все необходимые операции в componentDidMount() или других методах жизненного цикла. Сохранение render() чистым делает компонент легче для понимания.
Если вы не инициализируете состояние и не привязываете методы, вам не нужно реализовывать конструктор в вашем React-компоненте.
Конструктор для компонента React вызывается до того, как будет примонтирован. При реализации конструктора подкласса React.Component вы должны называть super(props) перед любым другим выражением. В противном случае this.props не будет определен в конструкторе, что может привести к багам.
Как правило, в React конструкторы используются только для двух целей:
** Избегайте копирования свойств в состояние! Это распространённая ошибка:**
componentDidMount() вызывается сразу после монтирования компонента (вставлено в DOM-дерево). Инициализация, требующая DOM-узлов, должна быть здесь. Если вам нужно загружать данные с удалённой конечной точки (endpoint), это хорошее место для создания экземпляра сетевого запроса.
componentDidUpdate() вызывается сразу после обновления. Этот метод не вызывается при первоначальной отрисовке.
Используйте данный метод как возможность работать с DOM при обновлении компонента. Это также хорошее место для выполнения сетевых запросов, если вы сравниваете текущие свойства с предыдущими свойствами (например, не нужно делать сетевой запрос, если свойство не изменилось).
Редко используемые методы жизненного цикла
Методы в этом разделе соответствуют малораспространённым случаям использования. Они удобны время от времени, но большинство ваших компонентов, вероятно, не нуждаются ни в одном из них. Вы можете увидеть большинство приведенных ниже методов на этой диаграмме жизненного цикла, если наверху страницы вы нажмете на чекбокс «Show less common lifecycles».
Вывод состояния приводит к подробному коду и затрудняет понимание ваших компонентов. Убедитесь, что вы хорошо знакомы с более простыми альтернативами:
Если вы хотите повторно вычислить некоторые данные только при изменении свойств, используйте помощник мемоизации вместо этого.
Этот метод не имеет доступа к экземпляру компонента. Если вы хотите, то можете повторно использовать код между getDerivedStateFromProps() и другими методами класса, извлекая чистые функции свойства и состояния компонента вне определения класса.
Этот не распространённый вариант использования, но он может быть в пользовательских интерфейсах, таких как цепочка сообщений в чатах, который должен обрабатывать позицию прокрутки особым образом.
Должно быть возвращено значение снимка (или null ).
Граница ошибок — это React-компоненты, которые перехватывают ошибки JavaScript в любом месте их дочернего дерева компонентов, логируют эти ошибки и отображают резервный интерфейс вместо разрушенного дерева компонентов. Граница ошибок отлавливают ошибки при отрисовке, в методах жизненного цикла и в конструкторах всего дерева под ними.
Классовый компонент становится границей ошибки, если он определяет этот метод жизненного цикла. Вызов setState() в нём позволяет зафиксировать необработанную JavaScript-ошибку в приведённом ниже дереве и отобразить резервный интерфейс. Используйте только граница ошибок для восстановления от неожиданных исключений; не пытайтесь использовать их для управления потоком.
Для получения дополнительной информации смотрите раздел Обработка ошибок в React 16.
Граница ошибок перехватывают только ошибки в компонентах ниже в их дереве. Граница ошибки не может поймать ошибку внутри себя.
This lifecycle is invoked after an error has been thrown by a descendant component. It receives the error that was thrown as a parameter and should return a value to update state.
getDerivedStateFromError() is called during the “render” phase, so side-effects are not permitted. For those use cases, use componentDidCatch() instead.
Устаревшие методы жизненного цикла
The lifecycle methods below are marked as “legacy”. They still work, but we don’t recommend using them in the new code. You can learn more about migrating away from legacy lifecycle methods in this blog post.
Это единственный хук жизненного цикла, вызываемый серверной отрисовкой.
Использование этого метода жизненного цикла часто приводит к ошибкам и несоответствиям.
UNSAFE_componentWillReceiveProps() вызывается до того, как смонтированный компонент получит новые свойства. Если вам нужно обновить состояние в ответ на изменения свойства (например, для его сброса), вы можете сравнить this.props и nextProps и выполнить переходы состояния с помощью this.setState() в этом методе.
Обратите внимание, что если родительский компонент заставляет ваш компонент повторно отрисовываться, этот метод будет вызываться, даже если свойства не изменились. Убедитесь в том, что сравниваете текущие и следующие значения, если вы только хотите обрабатывать изменения.
UNSAFE_componentWillUpdate() вызывается непосредственно перед отрисовкой при получении новых свойств или состояния. Используйте это как возможность выполнить подготовку до того, как произойдет обновление. Этот метод не вызывается при первоначальной отрисовке.
UNSAFE_componentWillUpdate() не будет вызываться, если shouldComponentUpdate() возвращает false.
В отличие от описанных выше методов жизненного цикла (которые React вызывает за вас), ниже приведены методы, которые вы можете вызывать из своих компонентов.
setState () ставит в очередь изменения в состояние компонента и указывает React, что этот компонент и его дочерние элементы должны быть повторно отрисованы с обновлённым состоянием. Это основной метод, который вы будете использовать для обновления пользовательского интерфейса в ответ на обработчики событий и ответы сервера.
Думайте о setState() как о запросе, а не как о команде немедленного действия для обновления компонента. Для лучшей очевидной производительности React может задержать выполнение, а затем обновить несколько компонентов за один проход. React не гарантирует незамедлительного применения изменений в состоянии.
Первый аргумент — это функция updater со следующим определением:
Второй параметр setState() — необязательный колбэк, вызываемый после завершения работы setState и далее компонент будет повторно отрисован. Обычно вместо этого мы рекомендуем использовать componentDidUpdate() для подобной логики.
Вы можете опционально передать объект в качестве первого аргумента setState() вместо функции:
Следующий фрагмент кода выполняет поверхностное объединение stateChange в новое состояние, например, чтобы скорректировать количество товаров в корзине:
Эта форма записи setState() также асинхронна, и несколько вызовов в течение одного цикла могут быть объединены (сгруппированы) вместе. Например, если вы пытаетесь увеличить количество элементов более одного раза в одном цикле, результат будет эквивалентным следующему ниже коду:
Последующие вызовы будут переопределять значения из предыдущих вызовов в том же самом цикле, поэтому количество будет увеличиваться только один раз. Если следующее состояние зависит от предыдущего состояния, мы рекомендуем использовать форму функции для обновления, т.е. следующим образом:
Для более подробной информации смотрите:
Если props.color не предоставлен, по умолчанию будет установлено значение ‘синий’ :
Строка displayName используется для отладочных сообщений. Обычно вам не нужно явно указывать её, поскольку по умолчанию предполагается имя функции или класса, который определяет компонент. Возможно, вы захотите установить его явно, если хотите отобразить другое имя для целей отладки или когда вы создаёте компонент высшего порядка, смотрите раздел Обтекание отображаемого имени для легкой отладки для получения подробной информации.
this.props содержит свойства, которые были определены вызывающим элементом этого компонента. Смотрите раздел Компоненты и реквизит для ознакомления со свойствами.
В частности, this.props.children — специальное свойство, обычно определяемое дочерними тегами в JSX-выражении, а не в самом теге.
Состояние содержит данные, конкретные для этого компонента, которые могут измениться со временем. Состояние определёно пользователем, и оно должно быть простым объектом JavaScript.
Если какое-либо значение не используется для отрисовки или потока данных (например, идентификатор таймера), вам не нужно вставлять его в состояние. Такие значения могут быть определены как поля экземпляра компонента.
Дополнительную информацию о состоянии смотрите в разделе Состояние и жизненный цикл.







