Вопросы для собеседования по хукам React

от автора

Что такое хуки в React?

Хуки — это новая функция, добавленная в React v16.8. Хуки позволяют использовать все возможности React без написания классовых компонентов. Например, до версии 16.8 для управления состоянием компонента нам нужен классовый компонент. Теперь мы можем сохранять состояние в функциональном компоненте с помощью хука useState.

Будут ли хуки React работать внутри классовых компонентов?

Нет.

Зачем были введены хуки в React?

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

Классовые компоненты не очень хорошо минимизируются, а также делают горячую перезагрузку ненадежной. Это еще одна причина для создания хуков.

Другая причина в том, что не существует конкретного способа повторно использовать логику компонента, наделенного состоянием. Несмотря на то, что HOC (Higher-Order Component) и шаблоны Render Props (метод передачи props от родителя ребенку, используя функцию или замыкание) решают эту проблему, здесь требуется изменить код классового компонента. Хуки позволяют совместно использовать логику с отслеживанием состояния без изменения иерархии компонентов.

Четвертая причина заключается в том, что в сложном классовом компоненте связанный код разбросан по разным методам жизненного цикла. Например, в случае загрузки данных (fetch) мы делаем это в основном в componentDidMount() и componentDidUpdate(). Другой пример: в случае слушателей событий мы используем componentDidMount() для подписки на события и componentWillUnmount() для отмены подписки. Вместо этого хуки помогают объединить связанный код.

Как работает хук useState? Какие аргументы принимает этот хук и что он возвращает?

Хук useState — это функция, которая используется для хранения состояния в функциональном компоненте. Он принимает аргумент как начальное значение состояния и возвращает массив с 2 элементами. Первый элемент — это текущее значение состояния. Второй элемент — это функция обновления состояния.

Сначала мы импортируем useState из React

import React, {useState} from "react";

Затем мы используем useState, например:

const [currentStateValue, functionToUpdateState] = useState(initialStateValue);

Задача на использование useState

У нас есть классовый компонент с состоянием. Каждый раз, когда нажимается кнопка в компоненте, счетчик увеличивается.

class Counter extends Component {     state = {         count: 0,     };      incrementCount = () => {         this.setState({             count: this.state.count + 1,         });     };      render() {         return (             <div>             <button onClick={this.incrementCount}>Count: {this.state.count}</button>         </div>     	);     } }

Перепишите этот компонент, используя хуки React.

Решение

import React, { useState } from "react";  function Counter() {     const [count, setCount] = useState(0);      return (         <div>             <button                 onClick={() => {                     setCount(count + 1);                 }}             >                 Count: {count}             </button>         </div>     ); }

Задача на использование useState 2

Ниже у нас есть классовый компонент. Он содержит код для обновления состояния на основе предыдущего значения состояния.

class Counter extends Component {     state = { count: 0 };      incrementCount = () => {         this.setState((prevState) => {             return {                 count: prevState.count + 1,             };         });     };      decrementCount = () => {         this.setState((prevState) => {             return {                 count: prevState.count - 1,             };         });     };      render() {         return (             <div>                 <strong>Count: {this.state.count}</strong>                 <button onClick={this.incrementCount}>Increment</button>                 <button onClick={this.decrementCount}>Decrement</button>             </div>         );     } }

Перепишите вышеуказанный код, используя хуки React.

Решение.

Можно обновить значение переменной состояния, просто передав новое значение в функцию обновления или передав callback. Второй способ с передачей callback-функции безопасен в использовании.

import React, { useState } from "react";  function Counter() {     const [count, setCount] = useState(0);      const incrementCount = () => {         setCount((prevCount) => {             return prevCount + 1;         });     };      const decrementCount = () => {         setCount((prevCount) => {             return prevCount - 1;         });     };      return (         <div>             <strong>Count: {count}</strong>             <button onClick={incrementCount}>Increment</button>             <button onClick={decrementCount}>Decrement</button>         </div>     ); }

Задача на использование useState 3

Здесь у нас есть классовый компонент, который обновляет состояние, используя ввод из формы.

export class Profile extends Component {     state = {         name: "Backbencher",         age: 23,     };      onNameChange = (e) => {         this.setState({             name: e.target.value,         });     };      onAgeChange = (e) => {         this.setState({             age: e.target.value,         });     };      render() {         return (             <div>                 <form>                     <input                         type="text"                         value={this.state.name}                         onChange={this.onNameChange}                     />                     <input                         type="number"                         value={this.state.age}                         onChange={this.onAgeChange}                     />                     <h2>                         Name: {this.state.name}, Age: {this.state.age}                     </h2>                 </form>             </div>         );     } }

Перепишите этот компонент, используя хуки React.

Решение

import React, { useState } from "react";  function Profile() {     const [profile, setProfile] = useState({         name: "Backbencher",         age: 23,     });      const onNameChange = (e) => {         setProfile({ ...profile, name: e.target.value });     };      const onAgeChange = (e) => {         setProfile({ ...profile, age: e.target.value });     };      return (         <div>             <form>                 <input type="text" value={profile.name} onChange={onNameChange} />                 <input type="text" value={profile.age} onChange={onAgeChange} />                 <h2>                     Name: {profile.name}, Age: {profile.age}                 </h2>             </form>         </div>     ); }

Функция обновления состояния в useState() не выполняет автоматическое слияние, если в состоянии хранится объект. Но в случае использования метода setState() в классовых компонентах происходит автоматическое слияние.

Здесь мы объединяем свойства объекта с помощью spread оператора JavaScript.

Каковы различия в использовании хуков и классовых компонентов в отношении управления состоянием?

При использовании setState() в классовых компонентах переменная состояния всегда является объектом. В то время как переменная состояния в хуках может иметь любой тип, например, число, строку, логическое значение, объект или массив.

Когда переменная состояния является объектом, setState() в классовых компонентах автоматически объединяет новое значение с объектом состояния. Но в случае функции обновления состояния в useState() нам нужно явно объединить обновленное свойство объекта с помощью spread оператора.

Зачем нужен хук useEffect?

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

Задача на использование useEffect

Вот классовый компонент, который печатает Boom в консоли каждый раз, когда он монтируется или обновляется.

export class Banner extends Component {     state = {         count: 0,     };      updateState = () => {         this.setState({             count: this.state.count + 1,         });     };      componentDidMount() {         console.log("Boom");     }      componentDidUpdate() {         console.log("Boom");     }      render() {         return (             <div>                 <button onClick={this.updateState}>State: {this.state.count}</button>             </div>         );     } }

Удалите избыточные выражения console.log с помощью хуков React.

Решение.

componentDidMount() и componentDidUpdate() — это методы жизненного цикла. Такие побочные эффекты можно сделать с помощью хука useEffect. Хук useEffect — это функция, которая принимает callback. Этот callback вызывается каждый раз, когда происходит рендеринг.

Код может быть переписан таким образом:

import React, { useState, useEffect } from "react";  function Banner() {     const [count, setCount] = useState(0);      useEffect(() => {         console.log("Boom");     });      const updateState = () => {         setCount(count + 1);     };      return (         <div>             <button onClick={updateState}>State: {count}</button>         </div>     ); }

Задача на использование useEffect 2

Понять данный код:

function Banner() {     const [count, setCount] = useState(0);     const [name, setName] = useState("");      useEffect(() => {         console.log("Счетчик обновлен");     });      return (         <div>             <button onClick={() => setCount(count + 1)}>State: {count}</button>             <input                 type="text"                 value={name}                 onChange={(e) => setName(e.target.value)}             />         </div>     ); }

Код печатает сообщение «Счетчик обновлен» даже при обновлении значения в текстовом поле. Как мы можем отображать сообщение только при обновлении состояния счетчика?

Решение.

Функция useEffect принимает второй параметр, который должен быть массивом. В этом массиве нам нужно передать props или состояние, за которым нам нужно следить. Эффект выполняется только в том случае, если эти свойства или состояние, упомянутые в массиве, изменяются. Поэтому в нашем коде мы добавляем второй аргумент и указываем только значение count в массиве.

Вот обновленный код useEffect:

useEffect(() => {     console.log("Count is updated"); }, [count]);

Задача на использование useEffect 3

У нас есть классовый компонент, который обновляет время каждую секунду. Он использует componentDidMount() для установки таймера.

export class Clock extends Component {     state = {         date: new Date(),     };      componentDidMount() {         setInterval(() => {             this.setState({                 date: new Date(),             });         }, 1000);     }      render() {         return <div>{this.state.date.toString()}</div>;     } }

Перепишите код выше с помощью хуков React.

Решение.

componentDidMount() — это метод жизненного цикла, который выполняется только один раз в жизненном цикле компонента. Мы используем useEffect, чтобы вызвать эффекты componentDidMount(). Но useEffect запускается при каждом обновлении props или состояния. Чтобы предотвратить это, мы используем второй аргумент-массив useState. Мы оставляем этот массив пустым. Итак, для React нет никаких переменных состояния или props, за которыми нужно следить. Следовательно, useEffect запускается только один раз, как componentDidMount().

Вот код с использованием хуков React.

function Clock() {     const [date, setDate] = useState(new Date());      useEffect(() => {         setInterval(() => {             setDate(new Date());         }, 1000);     }, []);      return <div>{date.toString()}</div>; }

Задача на использование useEffect 4

У нас есть фрагмент кода из классового компонента, который регистрирует и удаляет слушатель событий.

componentDidMount() {     window.addEventListener("mousemove", this.handleMousePosition); }  componentWillUnmount() {     window.removeEventListener("mousemove", this.handleMousePosition); }

Перепишите этот код в стиле хуков React.

Решение.

Чтобы использовать функциональность метода жизненного цикла componentWillUnmount() в хуке useEffect нужно вернуть callback с кодом, который необходимо выполнить при размонтировании компонента.

useEffect(() => {     window.addEventListener("mousemove", handleMousePosition);      return () => {         window.removeEventListener("mousemove", handleMousePosition);     } }, []);

Задача на использование useContext

Вот фрагмент кода из компонента Context.Consumer.

import { NameContext, AgeContext } from "./ProviderComponent";  export class ConsumerComponent extends Component {     render() {         return (             <NameContext.Consumer>                 {(name) => {                     return (                         <AgeContext.Consumer>                             {(age) => (                                 <div>                                     Name: {name}, Age: {age}                                 </div>                             )}                         </AgeContext.Consumer>                     );                 }}             </NameContext.Consumer>         );     } }

Перепишите ConsumerComponent, используя хук useContext.

Решение.

Хуки можно использовать только в функциональном компоненте.

ConsumerComponent можно переписать так:

function ConsumerComponent() {     const name = useContext(NameContext);     const age = useContext(AgeContext);      return (         <div>             Name: {name}, Age: {age}         </div>     ); }

ссылка на оригинал статьи https://habr.com/ru/post/534632/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *