Глибоке занурення у React Hooks з useContext та useReducer

28 хв. читання

У статті розберемося, як useContext та useReducer допомагають зробити застосунки на React і управління їхнім станом чистим та ефективним.

З новим Hooks API та його фічами зникає потреба користуватися такими бібліотеками управління станом, як Redux чи Flux. Ба більше, тепер ніяких компонентів-класів — можна обійтися лише функціональними компонентами.

Почнемо з огляду Context API, яке передувало хукам, а також найкращих практик та їх реалізацій в React-застосунках. Зробимо рефакторинг нашого прикладу з використанням useContext. Потім сфокусуємось на концепціях функції reducer та реалізації useReducer для управління складним станом. Щоб закріпити навички, скомбінуємо ці хуки при створенні інструменту управління станом на чистому React.

Ця стаття для вас, якщо...

Ви React-розробник і маєте базове розуміння:

  • потоку даних в React;
  • хуків useState та useEffect;
  • інструментів управління глобальним станом (на зразок Redux та Flux) і патернів;
  • React Context API.

Чому навчимо

  1. Пояснимо поняття контексту.
  2. Визначимо, коли реалізовувати контекст.
  3. Реалізуємо контекст за допомогою хуку useContext.
  4. Визначимо, коли використовувати useReducer.
  5. Реалізуємо useReducer для управління станом компонентів.
  6. Використаємо useReducer та useContext як інструмент для управління станом застосунку.
  7. Зробимо рефакторинг коду з новими фічами.

Огляд проекту

Наприкінці статті отримаємо інструмент управління станом для перегонів, що реалізовуватиме useReducer та useContext. Спочатку ми не будемо використовувати бібліотеку управління станом або Context чи Hooks API. Передаватимемо логіку через props та використовуватимемо Render Props. Сфокусуємось на рефакторингу тих частин коду, де потрібно реалізувати useContext та useReducer для управління станом.

Повний код проекту можна знайти у репозиторії.

У чому суть застосунку

У перегонах можуть бути декілька заїздів та декілька учасників (користувачів). Не всі користувачі беруть участь у заїзді. Застосунок передбачає доступ адміністратора до списку перегонів та користувачів кожного заїзду. Коли адмін входить до облікового запису та обирає перегляд перегонів, він може побачити усі перегони заїзду. Обираючи певний заїзд, можна побачити зареєстрованих у ньому користувачів. І навпаки: адмін може переглянути користувача та заїзди, в яких він брав участь.

Базові хуки

Ця частина для вас, якщо ви не знайомі з хуками в React. Хуки дають можливість отримати доступ до стану та методів життєвого циклу, а також передбачають багато інших трюків для функціональних компонентів. Більше жодних компонентів-класів. Якщо ви шукаєте надійну основу для початку, зупиніться на useState та useEffect.

  • useState: дозволяє отримати та контролювати стан всередині функціональних компонентів. До його появи лише компоненти-класи мали доступ до стану.
  • useEffect: передбачає абстрактний доступ до методів життєвого циклу React всередині функціональних компонентів. Ви не викликатимете індивідуальні методи життєвого циклу за назвою, але отримаєте аналогічний контроль з додатковим функціоналом та більш чистим кодом.

Приклад:

import React, { useState, useEffect } from 'react';

export const SomeComponent = () => {
  const [ DNAMatch, setDNAMatch ] = useState(false);
  const [ name, setName ] = useState(null);

  useEffect(() => {
    if (name) {
      setDNAMatch(true);
      setName(name);
      localStorage.setItem('dad', name);
    }
  }, [ DNAMatch ]);

  return (
    // ...
  );
};

useContext

Перед зануренням у useContext оглянемо Context API та його реалізацію до появи Hooks API. Так нам простіше буде оцінити useContext та визначити, коли його використовувати.

Огляд Context API

Відома пастка в React — доступ до об'єкта глобального стану. Коли дані треба отримати нижче за деревом компонентів — у темах, стилях UI або авторизації користувача — стає все складніше передавати їх через props. Так компоненти обтяжуються зайвими даними. Тут на допомогу приходять бібліотеки управління станом, на зразок Redux чи Flux. Вони дуже потужні, але викликають труднощі при налаштуванні, організації та розумінні, що саме потрібно реалізувати.

У цьому прикладі продемонструємо API, яке передувало контексту, коли props передавалися глибоко дочірніми компонентами:

// жодного контексту - лише передаємо prop
import React, { Component, useReducer, useContext } from 'react';

const Main = (props) => (
  <div className={'main'}>
    {/* // Main потрібен ListContainer для передачі даних */}
    <List
      isAuthenticated={props.isAuthenticated}
      toggleAuth={props.toggleAuth}
    />
  </div>
);

const List = ({ isAuthenticated, toggleAuth, shake }) => (
  isAuthenticated
    ? (
      <div className={'title'} >
        "secure" list, check.
      </div >)
    : (
      <div className={'list-login-container'}>
        {/* // List потрібен компонент AdminForm для передачі даних */}
        <AdminForm shake={shake} toggleAuth={toggleAuth} />
      </div>)
);

class AdminForm extends Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event, toggleAuth) {
    event.preventDefault();
    return toggleAuth(true);
  }

  render() {
    return (
      <div className={'form-container'}>
        <form
          onSubmit={event => this.handleSubmit(event, this.props.toggleAuth)}
          className={'center-content login-form'}
        >
          // ... логіка форми
        </form>
      </div>
    );
  }
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isAuthenticated: false,
    };
    this.toggleAuth = this.toggleAuth.bind(this);
  }

  toggleAuth(isAuthenticated) {
    return isAuthenticated
      ? this.setState({ isAuthenticated: true })
      : alert('Bad Credentials!');
  }

  render() {
    return (
      <div className={'App'}>
        <Main
          isAuthenticated={this.state.isAuthenticated}
          toggleAuth={this.toggleAuth}
        >
        </Main>
      </div>
    );
  }
}

export default App;

Реалізація Context API

Context API допомагає розв'язати проблеми з глобальним станом та перевантаженням зайвими даними дочірніх компонентів. Проведемо рефакторинг нашого коду з Context API.

// CONTEXT API
import React, { Component, createContext } from 'react';
// Ми використали 'null' , тому що потрібні дані
// знаходяться у стані компоненту App 
const AuthContext = createContext(null);
// Можна також використати деструктуризацію
// const { Provider, Consumer } = createContext(null)
const Main = (props) => (
  <div className={'main'}>
    <List />
  </div>
);

const List = (props) => (
  <AuthContext.Consumer>
    auth => {
      auth
        ? (
            <div className={'list'}>
              // ... пробігаємось конфіденційними даними для їх організації 
            </div>
          )
        : (
            <div className={'form-container'}>
               //List потребує компонента для передачі даних
              <AdminForm />
            </div>
          )
    }
  </AuthContext.Consumer>);

class AdminForm extends Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(event, toggleAuth) {
    event.preventDefault();
    return toggleAuth(true);
  }

  render() {
    return (
      <AdminContext.Consumer>
        {state => (
          <div>
            <form
              onSubmit={event => this.handleSubmit(event, state.toggleAuth)}
              className={'center-content login-form'}
            >
              // ... логіка форми
            </form>
          </div>
        )}
      </AdminContext.Consumer>
    );
  }
}

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isAuthenticated: false,
    };
    this.toggleAuth = this.toggleAuth.bind(this);
  }

  toggleAuth(isAuthenticated) {
    this.setState({ isAuthenticated: true });
  }

  render() {
    return (
      <div>
        <AuthContext.Provider value={this.state.isAuthenticated}>
          <Main />
        </AuthContext.Provider>
      </div>
    );
  }
}

export default App;

Такий метод використання Context API також працюватиме з хуками.

Особливості Context API

Варто враховувати деякі моменти при реалізації контексту. Context API повторно рендерить усі компоненти-нащадки provider. Якщо ви не будете обережними, все може закінчитися повторним рендерингом усього застосунку при кожному кліку або натисканні клавіші. Які рішення?

Будьте уважні при огортанні компонентів у provider. Створіть новий компонент, який приймає лише дочірні props. Перемістіть provider та його дані у створений компонент. Так дочірні props у provider не змінюватимуться при повторному рендерингу.

// Navigate.js
import React, { useReducer, useContext } from 'react';

const AppContext = React.createContext();

const reducer = (state, action) => {
  switch (action.type) {
    case 'UPDATE_PATH': return {
      ...state,
      pathname: action.pathname,
    };
    default: return state;
  }
};


export const AppProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, {
    pathname: window.location.pathname,
    navigate: (pathname) => {
      window.history.pushState(null, null, pathname);
      return dispatch({ type: 'UPDATE_PATH', pathname });
    },
  });

  return (
    <AppContext.Provider value={state}>
      {children}
    </AppContext.Provider>
  );
};

export const LinkItem = ({ activeStyle, ...props }) => {
  const context = useContext(AppContext);
  console.log(context, 'CONTEXT WTF?@');

  return (
    <div>
      <a
        {...props}
        style={{
          ...props.style,
          ...(context.pathname === props.href ? activeStyle : {}),
        }}
        onClick={(e) => {
          e.preventDefault();
          context.navigate(props.href);
        }}
      />
    </div>
  );
};

export const Route = ({ children, href }) => {
  const context = useContext(AppContext);
  return (
    <div>
      {context.pathname === href ? children : null}
    </div>
  );
};

Якщо ви скористаєтесь описаним вище підходом, то не отримаєте небажаних повторних рендерів (усе знаходиться в render props <AppProvider>).

// App.js
import React, { useContext } from 'react';
import { AppProvider, LinkItem, Route } from './Navigate.js';

export const AppLayout = ({ children }) => (
  <div>
    <LinkItem href="/participants/" activeStyle={{ color: 'red' }}>
      Participants
    </LinkItem>
    <LinkItem href="/races/" activeStyle={{ color: 'red' }}>
      Races
    </LinkItem>
    <main>
      {children}
    </main>
  </div>
);

export const App = () => {
  return (
    <AppProvider>
      <AppLayout>
        <Route href="/races/">
          <h1>Off to the Races</h1>
        </Route>
        <Route href="/participants/">
          <h1>Off with their Heads!</h1>
        </Route>
      </AppLayout>
    </AppProvider>
  );
};

Огорніть елементи-користувачі у Компоненти Вищого Порядку.

const withAuth = (Component) => (props) => {
  const context = useContext(AppContext)
  return (
    <div>
      {<Component {...props} {...context} />}
    </div>
  );
}

class AdminForm extends Component {
 // ...
}

export withAuth(AdminForm);

Context ніколи повністю не замінить бібліотеки управління станом, на зразок Redux. Усе тому, що з Redux можна проводити налагодження, налаштовувати бібліотеку для різного ПЗ, зберігати єдиний стан та слідувати практикам чистих функцій з connect. Context може зберігати стан; це дуже універсальний засіб, проте працює краще як provider даних, котрими користуються інші компоненти.

Render Props — чудове рішення для передачі даних, але може зробити дерево ваших компонентів ієрархічним. Як «пекло колбеків», тільки з HTML-тегами. Обирайте context, якщо дані повинні бути доступними глибоко всередині одного дерева компонентів. Такий підхід також підвищує продуктивність, оскільки батьківські компоненти не будуть повторно рендеритись при змінах даних.

Реалізація useContext

Хук useContext спрощує використання даних контексту та допомагає зробити компоненти повторно використовуваними. Щоб прояснити різницю з Context API, розглянемо компоненти, що використовують декілька контекстів. До появи хуків було складно повторно використовувати та розуміти компоненти, що використовували декілька контекстів. Це одна з причин, чому контекст слід використовувати з обережністю.

Продовжимо доповнювати наш приклад, додавши ще один контекст:

const AuthContext = createContext(null);
const ShakeContext = createContext(null);

class AdminForm extends Component {
  // ...
  render() {
    return (
      // використання декількох контекстів не виглядає привабливо
      <ShakeContext.Consumer>
        {shake => (
          <AdminContext.Consumer>
            {state => (
              // ... використовуємо!
            )}
          </AdminContext.Consumer>
        )}
      </ShakeContext.Consumer>
    );
  }
}

class App extends Component {
  //  ...
  <ShakeContext.Provider value={() => this.shake()}>
    <AuthContext.Provider value={this.state}>
      <Main />
    </AuthContext.Provider>
  </ShakeContext.Provider>
}

export default App;

Тепер уявіть дерево або ще більше контекстів....

Новий хук useContext не змінює концепцій контексту. Він лише передбачає ще один, привабливіший спосіб використання контексту. Хук стає у пригоді, якщо є компоненти, що використовують декілька контекстів.

Проведемо рефакторинг компоненту вище, використавши декілька контекстів з хуками.

const AdminForm = () => {
  const shake = useContext(ShakeContext);
  const auth = useContext(AuthContext);
  // отримуємо доступ до даних всередині кожного контексту
  // контекст досі повинен бути в області видимості компоненту, що його використовує 
  return (
    <div className={
      shake ? 'shake' : 'form-container'
    }>
      {
        auth.isAuthenticated
          ? user.name
          : auth.errorMessage
      }
    </div>
  );
};

І все! Тепер усе, що зберігає ваш контекст, буде в об'єкті, масиві або функції, до яких можна отримати доступ через useContext.

Час переходити до редьюсера, розглянути його переваги і реалізувати хук useReducer.

useReducer

Щоб краще орієнтуватися у використанні useReducer, оглянемо спочатку відповідні концепції та реальні приклади. Перейдемо від класу до функціональних компонентів з useState. Далі реалізуємо useReducer.

useReducer та useState: Хук useState дозволяє отримати доступ до однієї змінної стану всередині функціонального компонента з одним методом для її оновлення (наприклад, setCount). useReducer робить оновлення стану більш гнучким та явним. За тією ж аналогією Array.prototype.map та Array.prototype.reduce розв'язують подібні проблеми, але Array.prototype.reduce більш універсальний.

useState використовує useReducer під капотом.

У прикладі нижче продемонструємо незручності при використанні більше ніж одного useState-методу.

const App = () => {
  const [ isAdminLoading, setIsAdminLoading] = useState(false);
  const [ isAdmin, setIsAdmin] = useState(false);
  const [ isAdminErr, setIsAdminErr] = useState({ error: false, msg: null });

  // з таким підходом багато надлишкових змінних 
  const adminStatus = (loading, success, error) => {
    // ви повинні мати індивідуальний стан під рукою
    // так буде легше передавати свої наміри
    // і ви отримаєте попередньо ініціалізований результат
		if (error) {
      setIsAdminLoading(false); // можна встановлювати значення у reducer
      setIsAdmin(false);
      setIsAdminErr({
        error: true,
        msg: error.msg,
      });
      throw error;
    } else if (loading && !error && !success) {
      setIsAdminLoading(loading);
    } else if (success && !error && !loading) {
      setIsAdminLoading(false); // .. так виглядає заплутано
      setIsAdmin(true);
    }
  };
};
  • useReducer та Array.prototype.reduce: useReducer дуже схожий на Array.protoType.reduce. Обидві функції приймають колбек та початкове значення як параметри. У нашому прикладі назвемо їх reducer та initialState. Обидві функції повертають одне значення. Основна відмінність у тому, що useReducer більш функціональний при поверненні dispatch.
  • useReducer та Redux Reducer: Redux — набагато складніший інструмент для управління станом застосунку. Redux reducer зазвичай доступний через dispatch, props, приєднані компоненти-класи, actions та (можливо) сервіси; зберігається він у Redux store. З useReducer таких складнощів можна уникнути.

У найближчому майбутньому очікуються оновлення для багатьох пакетів React, які покращать розробку на React без Redux.

Можливості useReducer

Hooks API спрощує логіку компонентів, тому ви можете забути про ключове слово class. Хук можна ініціалізувати всередині ваших компонентів стільки разів, скільки необхідно. Як і з контекстом, складно визначити, коли час реалізовувати Hooks API.

Коли реалізовувати:

Можна розглядати використання useReducer, якщо:

  • компонент потребує складного об'єкта стану;
  • props компонента будуть використовуватись для визначення наступного значення стану компонента;
  • конкретний метод оновлення useState залежить від іншого значення useState.

В перелічених випадках useReducer спростить візуальну та логічну складність вашого коду, а також передбачить простір для розширення застосунку. Тобто ви мінімізуєте кількість логіки, залежної від стану, та зможете виразити ваш намір, а не результат при оновленні стану.

Переваги:

  • не потрібно розділяти компонент та контейнери;
  • функція dispatch доступна в межах видимості вашого компонента;
  • можливість маніпулювати станом при початковому рендерингу з третім аргументомinitialAction.

Поглянемо, який вигляд це все матиме на практиці.

Реалізація useReducer

Візьмемо невеликий фрагмент першого прикладу з контекстом:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isAuthenticated: false,
    };
    this.toggleAuth = this.toggleAuth.bind(this);
  }

  toggleAuth(success) {
    success
      ? this.setState({ isAuthenticated: true })
      : 'Potential errorists threat. Alert level: Magenta?';
  }

  render() {
    // ...
  }
}

Той самий код, але з використанням useState:

const App = () => {
  const [isAuthenticated, setAuth] = useState(false);

  const toggleAuth = (success) =>
    success
      ? setAuth(true)
      : 'Potential errorists threat. Alert level: Magenta?';

  return (
    // ...
  );
};

Очевидно, так код стає кращим. Оскільки ми додаємо більше логіки до застосунку, useReducer стане у пригоді. Почнемо додавати логіку з використанням useState.

const App = () => {
  const [isAuthenticated, setAuth] = useState(false);
  const [shakeForm, setShakeForm] = useState(false);
  const [categories, setCategories] = useState(['Participants', 'Races']);
  const [categoryView, setCategoryView] = useState(null);

  const toggleAuth = () => setAuth(true);

  const toggleShakeForm = () =>
    setTimeout(() =>
      setShakeForm(false), 500);

  const handleShakeForm = () =>
    setShakeForm(shakeState =>
      !shakeFormState
        ? toggleShakeForm()
        : null);

  return (
    // ...
  );
};

Досить складно. Як щодо useReducer?

// Тут наш reducer та initialState
// знаходяться за межами компоненту App 
const reducer = (state, action) => {
  switch (action.type) {
    case 'IS_AUTHENTICATED':
      return {
        ...state,
        isAuthenticated: true,
      };
    // ... можете перелічити інші case
    default: return state;
  }
};

const initialState = {
  isAuthenticated: false,
  shake: false,
  categories: ['Participants', 'Races'],
};

Використаємо ці змінні на початку компоненту App з useReducer.

const App = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const toggleAuth = (success) =>
    success
      ? dispatch({ type: 'IS_AUTHENTICATED' }) // BAM!
      : alert('Potential errorists threat. Alert level: Magenta?');

  return (
    // ...
  );
};

Такий код має значано кращий вигляд, але нам треба отримати логіку в контексті так, щоб використовувати її потім деревом компонентів. Поглянемо на провайдера авторизації та знайдемо, як передати йому логіку.

const App = () => {
  // ...
  <AdminContext.Provider value={{
    isAuthenticated: state.isAuthenticated,
    toggle: toggleAuth,
  }}>
    <Main />
  </AdminContext.Provider>
  // ...
};

Існує багато способів, але суть нашого підходу у тому, що є частина стану та функція, яка направляє дію, передану провайдеру. Дуже схоже на попередні приклади, за винятком розміщення та організації логіки.

А що, як ви передасте value={{ state, dispatch }} до prop вашого provider? А якщо ми вилучимо провайдер у функцію-обгортку? Ви можете позбавити ваш компонент логіки, тісно прив'язаної до інших компонентів. Окрім того, краще передавати конкретні наміри/дії, а не логіку.

Управління станом з хуками

Повернемося до наших App, reducer та initialState. Ми могли позбавлятися логіки, переданої через контекст. Натомість зараз потурбуємось про логіку компоненту, яка буде виконувати потрібні нам дії.

const App = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  // ...
  <AdminContext.Provider value={{ state, dispatch }}>
    <Main />
  </AdminContext.Provider>
  // ...
};

Тут необхідна логіка для потрібного функціоналу:

const AdminForm = () => {
  const auth = useContext(AdminContext);

  const handleSubmit = (event) => {
    event.preventDefault();
    authResult()
      ? dispatch({ type: 'IS_AUTHENTICATED' })
      : alert('Potential errorists threat. Alert level: Magenta?');
  };

  const authResult = () =>
    setTimeout(() => true, 500);

  return (
    <div className={'form-container'}>
      <form
        onSubmit={event => handleSubmit(event)}
        className={'center-content login-form'}
      >
        // ... інший код форми..
      </form>
    </div >
  );
};

Можна зробити краще. Якщо огорнемо компонент-провайдер в обгортку, отримаємо ефективний спосіб передачі даних без зайвих рендерингів.

// AdminContext.js
const reducer = (state, action) => {
  switch (action.type) {
    case 'IS_AUTHENTICATED':
      return {
        ...state,
        isAuthenticated: true,
      };
    // ... уявіть інші case
    default: return state;
  }
};

const initialState = {
  isAuthenticated: false,
  // ... уявіть ще більше значень!
};

const ComponentContext = React.createContext(initialState);

export const AdminProvider = (props) => {
  const [ state, dispatch ] = useReducer(reducer, initialState);

  return (
    <ComponentContext.Provider value={{ state, dispatch }}>
      {props.children}
    </ComponentContext.Provider>
  );
};
// App.js
// ...
const App = () => {
  // ...
  <AdminProvider>
    <Main />
  </AdminProvider>
  // ...
};

Тепер код більш організований, читабельний та доступний. Ним також простіше керувати, якщо застосунок зростатиме, а контексту з'являтиметься більше. Ви можете розділити файли контексту за компонентами або на свій розсуд.

Час вам самостійно спробувати різні варіанти та дослідити, що краще підходитиме вам, вашій команді та вашому застосунку.

Висновок

Ви обов'язково повинні розглянути використання хуків для управління станом у застосунку. useState — чудовий варіант для простого управління станом компонентів, натомість useReducer впорається із вкладеними компонентами. Чи буде «монолітний стан застосунку» поступатися «вкладеному стану»? Чи стануть мікростани з таким підходом більш керованою альтернативою мікросервісам?

Поки що хуки повністю не замінять Redux, але значно спростять вашу розробку, адже реалізувати useReducer досить просто.

Слід використовувати хуки для управління станом маленьких та середніх застосунків, а також очікувати нових ефективних способів реалізації управління станом (як Redux, але без зайвих витрат).

Хуки vs Redux: переваги та недоліки

Хочете реалізувати хуки для управління станом застосунку, замість більш структурованого фреймворка, як от Redux? Зверніть увагу на їх переваги та недоліки.

Переваги:

  1. Менше шаблонного коду;
  2. Більш швидка розробка (для маленьких і середніх застосунків);
  3. Безболісний та простіший рефакторинг;
  4. Більше контролю, проте слід враховувати проблеми продуктивності, пов'язані з повторним рендерингом;
  5. Зручно працювати з розкиданими частинами коду;
  6. Частина React Core API.

Недоліки:

  1. Менше підтримки інструментів розробника;
  2. Немає стандартного об'єкта глобального стану;
  3. Менше ресурсів і загальних правил;
  4. Не дуже підходить для новачків у React, які отримують переваги від структури Redux;
  5. Складно видаляти та передавати потрібні дані у великих застосунках.

Для новачків у React, які починають працювати над наявним проектом, хуки будуть більш зрозумілими. Якщо ви новачок і працюєте над малим або середнім застосунком, сміливо обирайте хуки. З іншого боку: розробникам буде легше долучитися до розробки більших застосунків, де складність абстраговано у вже налаштований Redux store.

Redux здебільшого отримує перевагу через його широке застосування та зрілість. Налагодження складного об'єкта стану з Redux буде простим, якщо ви знаєте, з чим маєте справу. Однак варто пам'ятати, що у великих застосунках сховища можуть стати надлишковими. Натомість хуки дозволяють нам розділити наші дані/ресурси/логіку так, що їх можна поширювати будь-де.

З Redux продуктивність буде кращою, якщо його реалізовують коректно і застосунок великий. Так, з хуками продуктивність страждатиме, проте іноді легше відстежувати та виправляти ці недоліки за допомогою перелічених трюків. Ви також можете реалізувати власну логіку, що імітуватиме mapStateToProps та mapDispatchToProps, для збільшення продуктивності застосунку у процесі зростання.

Ви самостійно вирішуєте, що краще пасуватиме у конкретній ситуації. Проте пам'ятайте: використання хуків та Redux не є взаємовиключним — можете успішно поєднувати обидва підходи для управління станом вашого застосунку.

Codeguida 9776
Приєднався: 1 місяць тому

Hosting Ukraine

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація