С выпуском React Clots я видел много постов, сравнивающих компоненты класса к функциональным компонентам. Функциональные компоненты не имеют ничего нового в реакции, однако не было возможно до версии 16.8.0 для создания элементальной компоненты с доступом к крючкам жизненного цикла, используя только функцию. Или это было?
Позвони мне педан (многие люди уже делают!) Но когда мы говорим о компонентах класса, мы технически говорим о компонентах, созданных функциями. В этом посте я хотел бы использовать React, чтобы продемонстрировать то, что на самом деле происходит, когда мы пишем класс в JavaScript.
Классы против функций
Во-первых, я хотел бы очень кратко показать, как обычно называют функциональными и классовыми компонентами относится друг к другу. Вот простой компонент, написанный как класс:
class Hello extends React.Component {
render() {
return Hello!
}
}
И вот он написан как функция:
function Hello() {
return Hello!
}
Обратите внимание, что функциональный компонент – это просто метод рендера. Из-за этого эти компоненты никогда не могли удерживать свое собственное состояние или выполнять любые побочные эффекты в точках во время их жизненного цикла. С момента реагирования 16.8.0 было возможно создать состоятельные функциональные компоненты благодаря крючкам, что означает, что мы можем повернуть компонент, как это:
class Hello extends React.Component {
state = {
sayHello: false
}
componentDidMount = () => {
fetch('greet')
.then(response => response.json())
.then(data => this.setState({ sayHello: data.sayHello });
}
render = () => {
const { sayHello } = this.state;
const { name } = this.props;
return sayHello ? {`Hello ${name}!`}
: null;
}
}
В функциональный компонент, как это:
function Hello({ name }) {
const [sayHello, setSayHello] = useState(false);
useEffect(() => {
fetch('greet')
.then(response => response.json())
.then(data => setSayHello(data.sayHello));
}, []);
return sayHello ? {`Hello ${name}!`}
: null;
}
Цель этой статьи не вступает в утверждение, что один лучше, чем другие, так как уже есть сотни постов на этой теме уже! Причина показать два компонента выше состоит в том, чтобы мы могли быть понятно о том, что реагирует на самом деле с ними.
В случае компонента класса React создает экземпляр класса с использованием Новый ключевое слово:
const instance = new Component(props);
Этот экземпляр является объектом. Когда мы говорим, что компонент является классом, что мы действительно имеем в виду, что это объект. Этот новый объектный компонент может иметь свое собственное состояние и методы, некоторые из которых могут быть методами жизненного цикла (рендеринг, компонентDIDMount и т. Д.), Которые реагируют на соответствующие точки во время жизни приложения.
С помощью функционального компонента React просто называет это как обычная функция (потому что это обычная функция!) И возвращает либо HTML или более компоненты реагирования.
Методы, с которыми обрабатывают состояние компонента и эффекты триггера при точках в течение жизненного цикла компонента теперь необходимо импортировать, если они обязательны. Эти работы полностью основаны на порядке, в котором они называются каждым компонентом, который их использует, поскольку они не знают, какой компонент им назвал. Вот почему вы можете вызвать только крючки на верхнем уровне компонента, и их нельзя назвать условно.
Функция конструктора
JavaScript не имеет классов. Я знаю, что это выглядит так, как у него есть классы, мы только что написали два! Но под капюшоном JavaScript не является классовым языком, он на основе прототипа. Классы были добавлены с помощью спецификации ECMAScript 2015 (также называемые ES6) и являются лишь более чистым синтаксисом для существующих функций.
Давайте пойти на переписывание компонента React Class без использования синтаксиса класса. Вот компонент, который мы собираемся воссоздать:
class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { count } = this.state;
this.setState({ count: count + 1 });
}
render() {
const { count } = this.state;
return (
<>
{count}
);
}
}
Это отображает кнопку, которая увеличивает счетчик при щелчке, это классика! Первое, что нам нужно создать – это функция конструктора, это будет выполнять те же действия, которые Конструктор Метод в нашем классе отдельно от звонка супер потому что это только классовое.
function Counter(props) {
this.state = {
count: 0
}
this.handleClick = this.handleClick.bind(this);
}
Это функция, которая реагирует, будет позвонить с новый ключевое слово. Когда функция называется с новый Это рассматривается как функция конструктора; новый объект создан, Это Направлена переменная, и функция выполняется с использованием нового объекта, где бы … Это упомянуто.
Далее нам нужно найти дом для оказывать и Handleclick Методы и для этого нам нужно поговорить о цепочке прототипа.
Цепь прототипа
JavaScript позволяет наследовать свойства и методы между объектами через что-то известный как цепь прототипа.
Ну, я говорю, наследство, но я на самом деле имею в виду делегацию. В отличие от других языков с классами, где недвижимость копируется из класса в его экземпляры, объекты JavaScript имеют внутреннюю ссылку Protoype, которая указывает на другой объект. Когда вы вызываете метод или попытайтесь получить доступ к свойству на объекте, JavaScript First проверяет свойство на самом объекте. Если он не может найти его там, то он проверяет прототип объекта (ссылка на другой объект). Если он все еще не может найти его, то он проверяет прототип прототипа и так на цепочке до тех пор, пока он не найдет его, либо не исчезнет из прототипов, чтобы проверить.
Вообще говоря, все объекты в JavaScript имеют Объект На вершине их прототипа цепи ; Вот как у вас есть доступ к таким методам, как TOSTRING и HasownProperty. на всех объектах. Цепь заканчивается, когда объект достигнут с нулевой Как его прототип, это обычно в Объект Отказ
Давайте попробуем сделать вещи более четкими с примером.
const parentObject = { name: 'parent' };
const childObject = Object.create(parentObject, { name: { value: 'child' } });
console.log(childObject);
Сначала мы создаем ParentObject Отказ Поскольку мы использовали объектный буквальный синтаксис, этот объект будет связан с Объект Отказ Далее мы используем Object.Create. Чтобы создать новый объект, используя ParentObject. как его прототип.
Теперь, когда мы используем console.log Напечатать ваш дочерний объект Мы должны увидеть:
Объект имеет два свойства, есть Имя Свойство, которое мы просто установили и __proto___ имущество. __Proto__ не является фактическим имуществом, как имя , это свойство аксессуаров для внутреннего прототипа объекта. Мы можем расширить их, чтобы увидеть наш прототип цепь:
Первый __proto___ Содержит содержимое ParentObject который имеет свой __proto___ Содержание содержимого Объект Отказ Это все свойства и методы, которые доступны для ChickObject. .
Может быть вполне запутанным, что прототипы находятся на недвижимости под названием __proto__ ! Важно осознавать, что __Proto__ только ссылка на связанный объект. Если вы используете Object.Create. Как мы имеем выше, связанный объект может быть все, что вы выбрали, если вы используете Новый Ключевое слово для вызова функции конструктора, эта ссылка происходит автоматически к функции конструктора Прототип имущество.
ОК, вернемся к нашему компоненту. С момента реагирования вызывает нашу функцию с Новый Ключевое слово, теперь мы знаем, что делать методы, доступные в цепочке прототипов нашего компонента, нам просто нужно добавить их в Прототип Свойство конструкторской функции, как это:
Counter.prototype.render = function() {
const { count } = this.state;
return (
<>
{count}
);
},
Counter.prototype.handleClick = function () {
const { count } = this.state;
this.setState({ count: count + 1 });
}
Статические методы
Это похоже на хорошее время, чтобы упомянуть статические методы. Иногда вы можете создать функцию, которая выполняет некоторые действия, которые относятся к экземплярам, которые вы создаете, но это не имеет смысла для доступности функции на каждом объекте Это Отказ При использовании с классами они называются статическими методами. Я не уверен, есть ли у них имя, когда не используется с классами!
Мы не использовали статические методы в нашем примере, но реагирует несколько статических методов жизненного цикла, и мы использовали один ранее с Object.Create. . Легко объявить статический метод в классе, вам просто нужно префикс метода с статический ключевое слово:
class Example {
static staticMethod() {
console.log('this is a static method');
}
}
И это одинаково легко добавить к функции конструктора:
function Example() {}
Example.staticMethod = function() {
console.log('this is a static method');
}
В обоих случаях вы называете такую функцию:
Example.staticMethod()
Расширение реагирования. Составная часть
Наш компонент почти готов, осталось всего две проблемы, чтобы исправить. Первая проблема заключается в том, что реагирование должна иметь возможность работать, является ли наша функция конструктора или просто обычная функция. Это потому, что ему нужно знать, назвать ли это с Новый ключевое слово или нет.
Дэн Абрамов написал отличный блог пост Об этом , но сократить длинную историю, отреагируйте искать свойство на компонент под названием ISREACTCOMPONENT Отказ Мы могли бы обойти это, добавив ISREACTCOMPONENT: {} к Counter.Prototype (Я знаю, вы ожидаете, что это будет логическим, но ISREACTCOMPONENT Значение – это пустой объект. Вам придется прочитать его статью, если вы хотите знать, почему!) Но это будет обманывать только систему, и она не решила бы проблему номер два.
В Handleclick Метод мы звоните в this.setState Отказ Этот метод не на нашем компоненте, оно «унаследовано» от Реагировать. Компонент вместе с ISREACTCOMPONENT Отказ Если вы помните Раздел цепи прототипа По сравнению с ранее мы хотим, чтобы наш экземпляр компонента сначала наследует методы на Counter.Prototype а затем методы от Реагировать. Компонент Отказ Это означает, что мы хотим связать свойства на Racte.component.prototype к Counter.prototype .__ Proto__ Отказ
К счастью, есть метод на Объект Что может помочь нам с этим:
Object.setPrototypeOf(Counter.prototype, React.Component.prototype);
Оно работает!
Это все, что нам нужно сделать, чтобы получить этот компонент работать с реагированием без использования синтаксиса класса. Вот код для компонента в одном месте, если вы хотите скопировать его и попробовать его для себя:
function Counter(props) {
this.state = {
count: 0
};
this.handleClick = this.handleClick.bind(this);
}
Counter.prototype.render = function() {
const { count } = this.state;
return (
<>
{count}
);
}
Counter.prototype.handleClick = function() {
const { count } = this.state;
this.setState({ count: count + 1 });
}
Object.setPrototypeOf(Counter.prototype, React.Component.prototype);
Как видите, не так приятно смотреть как раньше. В дополнение к созданию JavaScript более доступен для разработчиков, которые используются для работы с традиционными языками на основе классовых языков, синтаксис класса также делает код намного более читабельным.
Я не предлагаю, чтобы вы начну начнут писать свои комментарии реагирования таким образом (на самом деле, я бы активно не отговорил его!). Я только подумал, что это будет интересное упражнение, которое обеспечило бы некоторое представление о том, как работает наследство JavaScript.
Хотя вам не нужно понимать эти вещи, чтобы написать компоненты реагирования, это, безусловно, не может повредить. Я ожидаю, что будут случаи, когда вы исправляете сложную ошибку, в которой понимание того, как произведения прототипов наследования будут иметь всю разницу.
Я надеюсь, что вы нашли эту статью интересную и/или приятную. Вы можете найти больше постов, которые я написал в моем блоге на Hellocode.dev Отказ Спасибо.
Оригинал: “https://www.freecodecamp.org/news/have-you-used-react-object-components/”