Как да конвертирате компоненти на клас React във функционални компоненти с помощта на куки

През изминалия месец прекарах много време в работа с куки React. Създавам функционални компоненти, използвайки куки, вместо традиционните компоненти, базирани на клас. Няма да твърдя, че куките определено превъзхождат компонентите, базирани на класа. Но има огромно увеличаване на използваемостта, докато пишете вашите компоненти като функционални компоненти, особено след като тези функционални компоненти вече имат достъп до състояние и жизнен цикъл чрез използването на куки React.

В тази статия ще ви покажа как да преобразувате компонент React, базиран на клас, във функционален компонент, като вместо това замествате метода setState и жизнения цикъл, като компонентWillMount и компонентWillReceiveProps с куки React.

Затова нека първо изградим компонент React, базиран на класа, който използва методите на държавата, както и на жизнения цикъл. За това, разбира се, ще изградим традиционното приложение React Todo.

Нашето приложение на Todo ще изглежда така:

  • Въвеждане на текст, в който можете да въвеждате нови елементи на todo.
  • Бутон „Добавяне на Todo“, кликвайки върху който новият елемент на todo във въвеждането на текст е добавен към списъка с todo.
  • Списък, показващ всеки отделен елемент от todo.
  • Всеки отделен елемент на todo има свързано квадратче, което може да се използва за маркиране на продукта като завършен.
  • Todos са запазени за локално съхранение и се зареждат отново от локално хранилище при стартиране на приложението.

Нашите компоненти ще използват състояние, компонентDidMount, компонентDidUpdate и getDerivedStateFromProps жизнения цикъл. Няколко от тези методи (гледам ви getDerivedStateFromProps) се използват по изключително измислен начин, за да можете да демонстрирате как би изглеждала замяна на базата на куки.

Изграждане на приложението на клас Todo

Нашето приложение за todo ще бъде приложено като три различни компонента, като този:

Пълният код за тези примери можете да намерите в Github. Използвайте маркерите, за да се придвижвате между компонентите на класа и функционалните компоненти.

Нека първо да разгледаме компонента на TodoContainer. Този компонент поддържа пълното състояние на приложението, по всяко време в неговото състояние. Той има два метода addTodoItem и toggleItemCompleted, които се предават съответно като обратни повиквания към компонентите TodoInput и TodoItem и се използват за добавяне на нов елемент todo и маркират елемент като завършен или непълен.

В допълнение, компонентът TodoContainer използва метода на жизнения цикъл на компонентаDidMount за зареждане на запазени елементи от todo от localStorage. Ако няма запазени елементи на todo, празен списък се инстанцира като състояние на компонента TodoContainer. TodoContainer също използва метода на жизнения цикъл на компонентаDidUpdate, за да запази състоянието на компонента TodoContainer, до localStorage. По този начин, когато има промяна в състоянието на TodoContainer, той се поддържа до localStorage и може да бъде възстановен при повторно стартиране на приложението.

Събирайки всичко това заедно, компонентът на TodoContainer изглежда така:

След това нека разгледаме компонента TodoInput. Това е много прост компонент, който се състои от едно въвеждане на текст и бутон. Компонентът използва собственото си състояние, за да следи стойността на въвеждането на текст и при натискане на бутон предава този текст заедно с метода addTodoItem, предаден като опора onAdd от компонента TodoContainer.

Компонентът TodoInput изглежда така:

И накрая, компонента TodoItem. Това е друг прост компонент, който основно се състои от отметка, показваща дали елементът todo е попълнен, и етикет за този отметка, уточняващ текста на елемента todo. За да демонстрира използването на getDerivedStateFromProps, компонентът TodoItem приема като завършен компонент всички завършениItemIds от компонента TodoContainer и го използва, за да изчисли дали този конкретен TodoItem е пълен или не.

Компонентът TodoItem изглежда така:

Правила, които трябва да запомните, докато се закачате

За да демонстрираме различните куки React, които ще използваме за преобразуване на нашето приложение Todo, за да използваме функционални компоненти React, ще започна с простите куки и ще преминем към по-сложните. Но преди да направим това, нека разгледаме накратко най-важните правила, които трябва да имате предвид, докато използвате куки.

  • Куките трябва да се извикват само от функционален компонент React или от друга кука.
  • Едни и същи куки трябва да бъдат извиквани в един и същ ред, един и същ брой пъти по време на изобразяването на един компонент. Това означава, че куките не могат да бъдат извиквани в контурни или условни блокове и вместо това винаги трябва да се извикват на най-горното ниво на функцията.

Разсъжденията за тези правила за използване на куки са тема, която може да бъде статия сама по себе си. Ако се интересувате да прочетете повече за това, трябва да разгледате статията за правила на куките на официалния сайт на React.

Сега, когато знаем основните правила, които трябва да спазваме, докато използваме куки, нека да продължим и да започнем да преобразуваме нашето приложение Todo, за да използваме функционални компоненти.

Функционални компоненти за нашето приложение Todo

Най-простата и вероятно едната кука, която ще използвате най-много, е куката useState. Куката useState по принцип ви предоставя сетер и getter за манипулиране на едно свойство на състоянието на компонента. Куката useState има следния подпис:

Първият път, когато се извика куката, елементът на състоянието се инициализира с InitiValue. При следващи повиквания да useState, предварително зададената стойност ще бъде върната, заедно с метод на setter, който може да се използва за задаване на нова стойност за това конкретно състояние на държавата.

Затова нека преобразуваме компонента TodoInput във функционален компонент, използвайки куката useState.

Както можете да видите, ние използваме useState, за да получим стойността на свойството на текст и setText setter, получена от метода useState. Методите onTextChange и addTodoItem са променени, за да използват метода setText setter, вместо setState.

Може би сте забелязали, че обработващият събитията onChange за входа, както и бутонът Добавяне на Todo onClick обработвачи на събития, са анонимни функции, създадени по време на визуализацията. Както може би знаете, това не е много добро за производителност, тъй като препратката към функциите се променя между рендерите, като по този начин принуждава React да рендерира и входа, и бутона.

За да избегнем тези ненужни рендери, трябва да запазим позоваването на тези функции едно и също. Това е мястото, където следващата кука, която ще използваме useCallback, влиза в картината. useCallback има следния подпис:

където:

  • inlineFunctionDefinition - е функцията, към която искате да поддържате препратката между рендери. Това може да бъде вградена анонимна функция или функция, която се импортира от някъде другаде. Въпреки това, тъй като в повечето случаи бихме искали да се позоваваме на променливи състояния на компонента, ние ще дефинираме това като вградена функция, която ще ни позволи да осъществим достъп до променливите на състоянието, като използваме затваряния.
  • memoizationArguments - е масив от аргументи, към които се позовава функцията inlineFunctionDefinition. Първият път, когато се извика куката useCallback, паметта Аргументите се запазват заедно с inlineFunctionDefinition. При последващи повиквания, всеки елемент от новия масив memoizationArguments се сравнява със стойността на елемента в същия индекс в предварително запаметения масив memoizationArguments. Ако няма промяна, тогава запаметеното по-рано inlineFunctionDefinition се връща, като по този начин се запазва препратката и се предотвратява ненужното повторно изобразяване. Ако някой от параметрите се е променил, тогава inlineFunctionDefinition и новите memoizationArguments се записват и използват, като по този начин се променя позоваването на функцията и се гарантира повторно изобразяване.

Така че адаптиране на TodoInput да използва useCallback:

Сега, когато преобразихме TodoInput във функционален компонент, нека направим същото с TodoItem. Пренаписване чрез useState и useCallback, TodoItem става:

Ако сравните това с версията на компонентния клас на TodoItem, ще забележите, че в версията на функционалния компонент липсва getDerivedStateFromProps, който използвахме, за да определим дали този конкретен TodoItem е завършен или не. И така, коя кука ще използваме, за да го приложим?

Ами няма конкретна кука, която да реализира това. Вместо това ние ще трябва да приложим това като част от самата функция за изобразяване. Така че след като го внедрим, TodoItem изглежда така:

Може да забележите, че се обаждаме на метода за настройване на състоянието setCompleted по време на изобразяването на компонента. Докато пишем компоненти на клас, ние никога не извикваме setState в метода на изобразяване, така че защо това е приемливо във функционалните компоненти?

Това е позволено във функционалните компоненти специално, за да ни позволи да изпълняваме getDerivedStateFromProps-esque действия. Важно нещо, което трябва да се има предвид, е да се гарантира, че вътре във функционален компонент, ние винаги наричаме методи за определяне на състоянието вътре в условен блок. В противен случай ще завършим в безкраен цикъл.

Моля, обърнете внимание, че начинът, по който съм реализирал проверката isCompleted тук, е малко измислен, за да демонстрирам настройката на състоянието от функционалния компонент. В идеалния случай, завършеното състояние просто не би било използвано и изчислената стойност еCompleted ще бъде използвана за задаване на провереното състояние на квадратчето.

Накрая имаме само компонент на TodoContainer, който трябва да бъде преобразуван. Ще трябва да приложим състоянието, както и методите на жизнения цикъл на компонентаDidMount и компонентDidUpdate.

Тъй като вече видяхме за useState и всъщност нямам добро извинение да демонстрирам useReducer, ще се преструвам, че състоянието на компонента на TodoContainer е твърде сложно за управление на всяка собственост на държавата поотделно, използвайки useState, и че по-добрият вариант е да използвате useReducer.

Подписът на куката useReducer изглежда така:

където:

  • reducerFunction - е функцията, която приема съществуващото състояние и действие като вход и връща ново състояние като изход. Това трябва да е познато на всеки, който е използвал Redux и неговите редуктори.
  • InitiState - Ако не е предоставено състояниеInitializerFunction, това е обектът на първоначалното състояние на компонента. Ако е предоставено състояниеInitializerFunction, това се предава като аргумент на тази функция.
  • stateInitializerFunction - Функция, която ви позволява да извършвате мързелива инициализация на състоянието на компонента. Параметърът startState ще бъде предаден като аргумент на тази функция.

Така че конвертирате компонента на TodoContainer, за да използвате useReducer:

След това трябва да напишем състоянието на компонента TodoContainer в localStorage, когато компонентът се актуализира, подобно на това, което правехме в компонентDidUpdate. За да направите това, ще използваме куката useEffect. Куката useEffect ни позволява да зададем определено действие, което трябва да се извърши след завършване на всяко изобразяване на компонента. Подписът му е:

Докато се придържате към правилата на куките, за които говорихме по-рано, можете да поставите блок UseEffect навсякъде във вашия функционален компонент. Ако имате няколко блока useEffect, те ще се изпълняват последователно. Общата идея на useEffect е да извършва всички действия, които не влияят пряко на компонента в блока useEffect (пример: API повиквания, DOM манипулации и т.н.).

Можем да използваме useEffect, за да гарантираме, че след всяко отделно изобразяване на нашия TodoContainer състоянието на компонента се записва на localStorage.

След това трябва да възстановим състоянието от localStorage всеки път, когато компонентът е монтиран, подобно на това, което правим в компонентDidMount. Сега няма конкретни куки, които да изпълнят това, но можем да използваме функцията useReducer куки мързелива инициализация (която ще бъде извикана само при първото изобразяване), за да постигнем това.

Както можете да видите, четем запазеното предишно състояние от localStorage и го използваме, за да инициализираме състоянието на компонента TodoContainer, като по този начин имитираме това, което правехме в компонентDidMount. И с това преобразихме цялото си приложение todo във функционални компоненти.

В заключение…

В тази статия разбрах само няколко от куките, които са налични извън кутията в React. Освен тези извън куките куки, можем да напишем и собствени собствени куки за изпълнение на по-конкретни задачи.

Едно от ключовите постъпки от това малко упражнение за преобразуване на компоненти на клас във функционални компоненти е, че няма методи за преобразуване между методите на жизнения цикъл и куките React. Докато използването на useState и useReducer е доста подобно на setState, може да се наложи да промените начина, по който изпълнявате определени задачи, за да ги накарате да работят във функционален компонент (като getDerivedStateFromProps в нашето приложение Todo).

Като такова, преобразуването на компонент от клас React във функционален компонент е нещо, което може да е тривиално или доста сложно в зависимост от компонента. Освен ако не пишете нов компонент, наистина няма тежка нужда да преобразувате компонент от клас във функционален компонент, особено след като React ще продължи да поддържа и двете форми в обозримо бъдеще.

Въпреки това, ако се окаже, че трябва да преобразувате съществуващ компонент на клас във функционален компонент, надявам се тази статия да ви даде добра отправна точка. Ще се радвам да чуя вашето мнение за неща, които съм написал в тази статия.

Първоначално публикуван в asleepysamurai.com.