Всички основни концепции на React.js, заседнали в тази единствена медийна статия

Въведение, за да научите защо, какво и как да реагирате

Тази статия е адаптация на интерактивен наръчник на jsComplete.com/react-intro. Версията jsComplete има вградени кодови примери и връзки за навигация в съдържанието.
Преди да започнете, моля, обърнете внимание, че това е удобно за начинаещи ръководство, което обхваща концепциите, които класифицирам като основни за работа с React. Това не е пълно ръководство за React, а по-скоро пълно въведение.
В края на това ръководство изброявам няколко ресурса от следващото ниво за вас. Това ръководство ще проправи пътя за разбирането им.

React се дефинира като JavaScript библиотека за изграждане на потребителски интерфейси. Нека започнем с разговора за двете различни части на това определение.

React е „библиотека“ на JavaScript. Това не е точно „рамка“. Това не е цялостно решение и често ще трябва да използвате повече библиотеки с React, за да образувате някакво решение. React не предполага нищо за останалите части във всяко решение.

Рамките служат на чудесна цел, особено за младите отбори и стартъпите. Когато работите с рамка, за вас вече се вземат много интелигентни дизайнерски решения, което ви дава ясен път да се съсредоточите върху писането на добра логика на ниво приложение. Рамките обаче имат някои недостатъци. За опитни разработчици, работещи върху големи кодови бази, тези недостатъци понякога са прекъсвач на сделката.

Рамките не са гъвкави, въпреки че някои твърдят, че са. Рамка обикновено иска да кодирате всичко по определен начин. Ако се опитате да се отклоните от този начин, рамката обикновено се бори с вас за това. Рамките също обикновено са големи и пълни с функции. Ако трябва да използвате само малко парче от тях, така или иначе трябва да включите цялото нещо. Вярно е, че тази точка днес се променя, но все още не е идеална. Някои рамки стават модулни, което мисля, че е чудесно, но аз съм голям фен на чистата философия на Unix:

Пишете програми, които вършат едно нещо и го правят добре. Напишете програми, за да работите заедно.
- Дъг Макилрой

React следва философията на Unix, защото това е малка библиотека, която се фокусира само върху едно нещо и върху това, което прави това изключително добре. Това „едно нещо“ е втората част от дефиницията на React: Изграждане на потребителски интерфейси.

Потребителски интерфейс (UI) е всичко, което поставяме пред потребителите, за да могат те да взаимодействат с машина. Потребителските интерфейси са навсякъде - от простите бутони на микровълновата до таблото на космическа совалка. Ако устройството, което се опитваме да интерфейс, може да разбере JavaScript, можем да използваме React, за да опишем потребителски интерфейс за него. Тъй като уеб браузърите разбират JavaScript, можем да използваме React за описание на уеб потребителските интерфейси.

Обичам да използвам думата описвам тук, защото това е основното, което правим с React. Просто му казваме какво искаме! След това React ще изгради действителния потребителски интерфейс от наше име в уеб браузъра. Без React или подобни библиотеки ще трябва ръчно да създадем потребителски интерфейси с нативни уеб API и JavaScript и това не е толкова лесно.

Когато чуете твърдението, че „React е декларативен“, това е точно това. Описваме потребителските интерфейси с React и му казваме какво искаме (а не как да го направим). React ще се погрижи за „как“ и ще преведе декларативните ни описания (които пишем на езика React) в действителните потребителски интерфейси в браузъра. React споделя тази проста декларативна сила със самия HTML, но с React ставаме декларативни за HTML потребителски интерфейси, които представляват динамични данни, а не само статични данни.

Когато React беше пуснат, имаше много шумотевици по отношение на неговото изпълнение, защото той въведе интелигентната идея за виртуален DOM, който може да се използва за съгласуване на действителния DOM (и ще говорим за това в следващия раздел).

DOM е „Модел на обект на документ“. Това е програмният интерфейс на браузърите за HTML (и XML) документи, който ги третира като дървесни структури. API на DOM може да се използва за промяна на структурата, стила и съдържанието на документа.

Въпреки че представянето на React все още е една от най-важните причини, поради която днес е изключително популярна, аз не я класифицирам като „най-доброто“ в нея. Мисля, че React беше смяна на игри, защото създаде общ език между разработчиците и браузърите, който позволява на разработчиците да описват декларативно потребителските интерфейси и да управляват действия върху тяхното състояние, вместо действия върху техните DOM елементи. Това е просто езикът на резултатите от потребителския интерфейс. Вместо да измислят стъпки за описание на действията върху интерфейсите, разработчиците просто описват интерфейсите от гледна точка на „окончателно“ състояние (като функция). Когато действията се случват с това състояние, React се грижи за актуализиране на потребителските интерфейси в DOM въз основа на това (и прави това ефективно, както ще видим по-нататък).

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

Реагиращият език

Кажете, че имаме списък с TODO като този:

const todos: [
  {body: ‘Научете реагирайте основи’, готово: true},
  {body: 'Създаване на приложение за TODOs', направено: false},
  {body: 'Build a Game', изпълнено: false},
];

Този масив todos е началното състояние на вашия потребителски интерфейс. Ще трябва да изградите потребителски интерфейс, за да ги показвате и управлявате. Потребителският интерфейс ще има форма за добавяне на нови TODO, начин да маркирате TODO като завършен и начин да премахнете всички завършени TODO.

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

Списък на TODO
<Ул>
  {todos.map (todo =>)
    
  • {todo.body}   )}
  • // Други елементи на формата ...

    Все още не се притеснявайте за синтаксиса, но ако се чудите какво става, просто картографираме масив от JavaScript обекти в масив от React елементи. Повече за това скоро.

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

    Този ментален модел за моделиране на потребителския интерфейс въз основа на крайно състояние е по-лесен за разбиране и работа, особено когато изгледите имат много преходи на данни. Например, помислете за изгледа, който ви казва колко от приятелите ви са онлайн. „Състоянието“ на този изглед ще бъде само едно число от броя на приятелите в момента. Не се интересува, че преди малко трима приятели дойдоха онлайн, след това един от тях прекъсна връзката и след това още двама се присъединиха. Просто знае, че в този момент четирима приятели са онлайн.

    Реакция на примиряването на дървото

    Преди React, когато трябваше да работим с API на браузъра, известен като API на DOM, избягваме да обикаляме DOM дървото колкото е възможно повече и има причина за това. Всяка операция на DOM се извършва в една и съща единствена нишка, която е отговорна за всичко останало, което се случва в браузъра, включително реакции на потребителски събития като писане, превъртане, преоразмеряване и т.н.

    Всяка скъпа операция на DOM означава бавно и странно изживяване за потребителя. Изключително важно е вашите приложения да извършват абсолютно минималните операции и да ги пакетират, където е възможно. React излезе с уникална концепция, която да ни помогне да направим точно това!

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

    Когато кажем на React да актуализира дървото на елементите, които преди е представило, той генерира ново виртуално представяне на обновеното дърво. Сега React има 2 версии на дървото в памет!

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

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

    Освен езика, основан на декларативните резултати, и ефективното съгласуване на дърветата, ето някои от другите причини, поради които смятам, че React придоби огромната си популярност:

    • Работата с DOM API е трудна. React дава възможност на разработчиците да работят с „виртуален“ браузър, който е по-приятен от реалния браузър. Реагира по принцип действа като вашия агент, който ще извърши комуникацията с DOM от ваше име.
    • Често React получава етикета „Just JavaScript“. Това означава, че има много малък API за научаване и след това вашите JavaScript умения са това, което ви прави по-добър React разработчик. Това е предимство пред библиотеките с по-големи API. Също така, React API е предимно функции (и по желание класове, ако имате нужда от тях). Когато чуете, че изгледът на потребителския интерфейс е функция на вашите данни, в React това е буквално така.
    • Learning React се отплаща в голяма степен и за iOS и Android мобилни приложения. React Native ви позволява да използвате вашите умения React за изграждане на родните мобилни приложения. Можете дори да споделите някаква логика между вашите приложения за уеб, iOS и Android.
    • Екипът на React във Facebook тества всички подобрения и нови функции, които се запознават с React точно там във facebook.com, което увеличава доверието в библиотеката сред общността. Рядко се наблюдават големи и сериозни бъгове в изданията на React, защото те се пускат едва след задълбочени производствени тестове във Facebook. Реагира също така и други широко използвани уеб приложения като Netflix, Twitter, Airbnb и много други.

    Вашият първи пример на React

    За да видим практическата полза от процеса на съвместяване на дърветата и голямата разлика, която прави, нека работим чрез прост пример, фокусиран върху точно тази концепция. Нека генерираме и актуализираме дърво на HTML елементи два пъти, веднъж използвайки родния уеб API и след това използвайки React API (и неговата съвместяваща работа). За да запазя този пример прост, няма да използвам компоненти или JSX (разширението на JavaScript, което се използва популярно с React). Аз също ще извърша операцията за актуализиране в рамките на таймер за интервал на JavaScript. Така не пишем React приложения, но нека се съсредоточим върху една концепция наведнъж.

    Започнете с тази сесия jsComplete playground: jsdrops.com/react-dom1.

    В тази сесия прост HTML елемент се изобразява на дисплея чрез 2 метода:

    Метод №1: Директно използване на Web DOM API

    document.getElementById ('mountNode'). innerHTML = `
      
        Здравейте HTML    `;

    Метод №2: Използване на API на React

    ReactDOM.render (
      React.createElement (
        "Разделения"
        нула,
        "Здравей Реакт",
      ),
      document.getElementById ( "mountNode2"),
    );

    Методът ReactDOM.render и React.createElement са основните методи на API в приложението React. Всъщност уеб приложение React не може да съществува, без да се използват и двата метода. Нека да ги обясня накратко:

    ReactDOM.render

    Това е по принцип входната точка за приложение React в DOM на браузъра. Той има 2 аргумента:

    1. Първият аргумент е КАКВО да предам на браузъра. Това винаги е „Реагиращ елемент“.
    2. Вторият аргумент е КЪДЕ да направи този елемент React в браузъра. Това трябва да е валиден DOM възел, който съществува в статично представения HTML. Примерът по-горе използва специален елемент mountNode2, който съществува в зоната за показване на детската площадка (първият mountNode се използва за нативната версия).

    Какво точно е React елемент? Това е VIRTUAL елемент, описващ DOM елемент. Това е, което връща методът на API на React.createElement.

    React.createElement

    Вместо да работим с низове за представяне на DOM елементи (както в родния пример на DOM по-горе) в React, ние представяме DOM елементи с обекти, използвайки обаждания към метода React.createElement. Тези обекти са известни като Реактивни елементи.

    Функцията React.createElement има много аргументи:

    1. Първият аргумент е HTML "таг" за DOM елемента, който да представлява, което е div в този пример.
    2. Вторият аргумент е за всички атрибути (като id, href, заглавие и т.н.), които искаме да има елемент DOM. Простият div, който използваме, няма атрибути, затова използвахме нула там.
    3. Третият аргумент е съдържанието на DOM елемента. Там поставихме низ „Hello React“. Незадължителният трети аргумент и всички незадължителни аргументи след него формират списъка с деца за изобразения елемент. Елементът може да има 0 или повече деца.
    React.createElement може да се използва и за създаване на елементи от React компоненти.

    Реактивните елементи се създават в паметта. За да накараме React елемент да се покаже в DOM, използваме метода ReactDOM.render, който ще направи много неща, за да измисли най-оптималния начин да отразява състоянието на React елемент в действителното DOM дърво в браузъра.

    Когато изпълните 2 метода в тази сесия с кодове, ще видите поле "Hello HTML" и поле "Hello React":

    Размножаващи се реагиращи елементи

    Имаме два възела: единият се управлява директно с DOM API, а другият се контролира с React API (който от своя страна използва DOM API). Единствената основна разлика между начините, по които изграждаме тези два възла в браузъра, е, че в HTML версията използвахме низ за представяне на DOM дървото, докато във версията React използвахме чисти JavaScript обаждания и представяхме DOM дървото с обект вместо низ.

    Без значение колко сложен ще бъде HTML потребителският интерфейс, когато използвате React, всеки HTML елемент ще бъде представен с React елемент.

    Нека добавим още HTML елементи към този прост потребителски интерфейс. Нека добавим текстово поле за четене на данни от потребителя. За HTML версията можете просто да поставите маркера на новия елемент директно в шаблона:

    document.getElementById ('mountNode'). innerHTML = `
      'DIV>
        Здравейте HTML
        <вход />
      
    `;

    За да направите същото с React, можете да добавите още аргументи след третия аргумент за React.createElement по-горе. За да съответстваме на това, което е в родния пример на DOM досега, можем да добавим четвърти аргумент, който е поредното React.createElement обаждане, което прави приспадане:

    ReactDOM.render (
      React.createElement (
        "Разделение",
        нула,
        "Здравей Реакт",
        React.createElement ( "вход")
      ),
      document.getElementById ( "mountNode2"),
    );

    Нека също така представим текущото време и в двете версии. Нека го поставим в предварителен елемент (само за да му дадем моноспективен шрифт в детската площадка). Можете да използвате нов Date (). ToLocaleTimeString (), за да покажете обикновен времеви низ. Ето какво трябва да направите за родната версия на DOM:

    document.getElementById ('mountNode1'). innerHTML = `
      
        Здравейте HTML     <вход />     
     $ {new Date (). toLocaleTimeString ()} 

       `;

    За да направим същото в React, добавяме пети аргумент към елемента div на най-високо ниво. Този нов пети аргумент е поредното обаждане на React.createElement, този път използвайки предварителен маркер с новия низ Date (). ToLocaleTimeString () за съдържание:

    ReactDOM.render (
      React.createElement (
        "Разделения"
        нула,
        "Здравей Реакт",
        React.createElement ( "вход"),
        React.createElement (
          'пред',
          нула,
          нова дата (). toLocaleTimeString ()
        )
      ),
      document.getElementById ( "mountNode2)
    );

    И двете версии все още представят абсолютно същия HTML код в браузъра.

    Както вероятно мислите досега, използването на React е много по-трудно от простия и познат нашенски начин. Какво е, че React прави толкова добре, че си струва да се откажете от познатия HTML и да се наложи да научите нов API, за да напишете това, което може да бъде просто написано в HTML?

    Отговорът не е във визуализирането на първия HTML изглед. Става въпрос за това, което трябва да направим, за да актуализираме всеки съществуващ изглед в DOM.

    Актуализиране на реагиращите елементи

    Нека направим актуализация на DOM дърветата, които имаме досега. Нека просто направим отметка на времевия низ всяка секунда.

    Лесно можем да повторим обаждането на функцията на JavaScript в браузър, използвайки setInterval уеб таймера API. Нека да поставим всички наши DOM манипулации за двете версии във функция, да я назовем и да я използваме в setInterval призив, за да я повторим всяка секунда.

    Ето пълния краен код за този пример:

    // jsdrops.com/react-dom2
    const render = () => {
      document.getElementById ('mountNode'). innerHTML = `
        
          Здравейте HTML       <вход />       
     $ {new Date (). toLocaleTimeString ()} 
           `;
      ReactDOM.render (
        React.createElement (
          "Разделения"
          нула,
          "Здравей Реакт",
          React.createElement ('input', null),
          React.createElement (
            'пред',
            нула,
            нова дата (). toLocaleTimeString ()
          )
        ),
        document.getElementById ( "mountNode2)
      );
    };
    setInterval (рендер, 1000);

    Вижте резултата от изпълнението на този код на jsdrops.com/react-dom2 и забележете как времевият низ отчита всяка секунда и в двете версии. Сега актуализираме нашия потребителски интерфейс в DOM.

    Това е моментът, в който React потенциално ще ви взриви ума. Ако се опитате да напишете нещо в текстовото поле на родния вариант на DOM, няма да можете. Това е много очаквано, защото ние по принцип изхвърляме целия възел DOM на всеки кърлеж и го регенерираме. Ако обаче се опитате да въведете нещо в текстовото поле, което е изобразено с React, със сигурност можете да го направите!

    Въпреки че целият код за изобразяване на React е в рамките на таймера за отметка, React променя само съдържанието на предварителния елемент, а не на цялото DOM дърво. Ето защо полето за въвеждане на текст не беше регенерирано и ние успяхме да го напишем.

    Можете да видите различните начини, по които актуализираме визуално DOM, ако проверите двата DOM възли в панела с елементи на Chrome DevTools. Панелът с елементи на Chrome DevTools подчертава всички DOM елементи, които се актуализират. Ще видите как родната HTML версия регенерира целия си контейнер div # mountNode с всеки отметка, докато React интелигентно само регенерира предварителния маркер в своя контейнер div # mountNode2.

    Това е интелигентният алгоритъм на React в действие. Той актуализира само в главното DOM дърво това, което всъщност трябва да се актуализира, докато поддържа всичко останало същото. Този различен процес е възможен поради виртуалното представяне на DOM на React, което поддържа в паметта. Независимо колко пъти трябва да се регенерира изгледите на потребителския интерфейс, React ще отнесе в браузъра само необходимите „частични“ актуализации.

    Този метод не само е много по-ефективен, но и премахва голям слой сложност в начина, по който мислим за актуализиране на потребителските интерфейси. След като React направим всички изчисления за това дали трябва или не трябва да актуализираме DOM, ни позволява да се съсредоточим върху мисленето за нашите данни (състояние) и начина, по който да опишем потребителския интерфейс за тях. След това управляваме актуализациите на състоянието на данните според нуждите, без да се притесняваме за необходимите стъпки за отразяване на тези актуализации в действителния потребителски интерфейс в браузъра (защото знаем, че React ще направи точно това и ще го направи по ефективен начин!)

    React е свързан с компоненти

    В React описваме потребителски интерфейси, използвайки компоненти, които могат да се използват многократно, да се използват за използване и да са стационарни.

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

    Можете да мислите за компоненти като прости функции (във всеки език за програмиране). Ние наричаме функции с някакъв вход и те ни дават известен изход. Можем да използваме повторно функции при нужда и да съставяме по-големи функции от по-малки.

    Реактивните компоненти са абсолютно същите; входът им е набор от „реквизити“, а изходът им е описание на потребителски интерфейс. Можем да използваме повторно един компонент в множество потребителски интерфейси, а компонентите могат да съдържат други компоненти. Основната форма на компонента React всъщност е обикновена стара JavaScript функция.

    Някои компоненти на React са чисти, но можете също да въведете странични ефекти в даден компонент. Например, компонент може да промени HTML „заглавието“ на уеб страница, когато се монтира в браузъра или може да превърти изгледа на браузъра в определена позиция.

    Най-важното е, че React компонентът може да има частно състояние, което да съхранява данни, които могат да се променят през жизнения цикъл на компонента. Това частно състояние е неявна част от входа, който задвижва изхода на компонента и това всъщност дава името на React!

    Защо React е наречен „React“ все пак?
    Когато състоянието на компонента React (който е част от неговия вход) се промени, потребителският интерфейс, който представлява, (неговият изход) също се променя. Тази промяна в описанието на потребителския интерфейс трябва да бъде отразена в устройството, с което работим. В браузър трябва да актуализираме DOM дървото. В приложението React не правим това ръчно. React просто ще реагира на промените в състоянието и автоматично (и ефикасно) актуализира DOM при необходимост.

    Създаване на компоненти с помощта на функции

    Компонентът React - в най-простата си форма - е обикновена стара JavaScript функция:

    // jsdrops.com/bx1
    функционален бутон (подпори) {
      // Връща тук DOM / React елемент. Например:
      връщане <бутона тип = "подаване"> {props.label} ;
    }
    // За изобразяване на елемент Button в браузъра
    ReactDOM.render (<етикет на бутона = "Запазване" />, mountNode);

    Обърнете внимание как написах как изглежда HTML във върнатия изход на функцията Button по-горе. Това не е нито HTML, нито JavaScript и дори не е React. Това е JSX. Това е разширение към JavaScript, което ни позволява да пишем функционални обаждания в синтаксис, подобен на HTML.

    Продължете напред и опитайте да върнете всеки друг HTML елемент във функцията Button и вижте как се поддържат всички (например, върнете входен елемент или елемент textarea).

    JSX не е HTML

    JSX не се разбира от браузърите. Ако се опитате да изпълните функцията Button в обикновена конзола на браузъра, той ще се оплаче от първия символ в частта на JSX:

    Това, което браузърите разбират (имайки предвид библиотеката React) е React.createElementAPI повиквания. Същият пример на бутон може да бъде написан без JSX, както следва:

    // jsdrops.com/bx2
    функционален бутон (подпори) {
      върнете React.createElement (
        "Бутон",
        {type: "submit"},
        props.label
      );
    }
    ReactDOM.render (
      React.createElement (Бутон, {label: "Запазване"}),
      mountNode
    );

    Можете да използвате React по този начин (без JSX). Можете да изпълните функцията Button в браузър директно (след зареждане на библиотеката React) и нещата ще работят добре. Ние обаче обичаме да виждаме и да работим с HTML, вместо да се занимаваме с обаждания на функции. Кога за последен път създадохте уебсайт само с JavaScript и не използвате HTML? Можете, ако искате, но никой не прави това. Ето защо JSX съществува.

    JSX е основно компромис. Вместо да пишем компоненти на React, използвайки синтаксиса React.createElement, ние използваме синтаксис, много подобен на HTML и след това използваме компилатор, за да го преведем в React.createElement.

    Компилатор, който превежда една форма на синтаксис в друга, е известен като „транспилатор“. За превод на JSX можем да използваме транспилатори като Babel или TypeScript. Например, игралната площадка jsComplete използва TypeScript, за да транспилира всеки JSX, който поставите в нея. Когато използвате create-react-app, генерираното приложение ще използва вътрешно Babel, за да транспилира вашия JSX.

    Можете да използвате babeljs.io/repl/, за да видите в какво се конвертира всеки JSX синтаксис за React, но JSX може да се използва и самостоятелно. Това не е нещо само за Реакция.

    Така че React компонент е JavaScript функция, която връща React елемент (обикновено с JSX). Когато се използва JSX, синтаксисът

    Името трябва да започва с главна буква

    Обърнете внимание как нарекох компонента „Бутон“. Първата буква е главна буква всъщност е изискване, тъй като ще се занимаваме с комбинация от HTML елементи и React елементи. JSX компилатор (като Babel) ще счита всички имена, които започват с малка буква, като имена на HTML елементи. Това е важно, защото HTML елементите се предават като низове към React.createElement, докато React елементите трябва да бъдат предадени като променливи:

    Продължете напред и опитайте да назовете компонента React „бутон“ вместо „Бутон“ и вижте как ReactDOM ще пренебрегне напълно функцията и ще направи обикновен празен HTML бутон елемент.

    // jsdrops.com/bx3
    // Грешно:
    функционален бутон () {
      връщане 
    My Fancy Button
    ; };
    // Следното ще направи HTML бутон
    // (и игнорирайте функцията за фантазия бутон)
    ReactDOM.render (<бутон />, mountNode);

    Първият аргумент е обект на „подпори“

    Точно както на HTML елементите могат да се присвояват атрибути като id или заглавие, така и React елементът може да получи списък от атрибути, когато се визуализира. Елементът Бутон по-горе (jsdrops.com/bx2) получи атрибут на етикет. В React списъкът на атрибутите, получени от React елемент, е известен като подпори. Функционален компонент React получава този списък като първи аргумент. Списъкът се предава като обект с ключове, представящи имената на атрибутите и стойностите, представящи стойностите, присвоени им.

    Когато използвате функционален компонент, не е нужно да назовавате обекта, съдържащ списъка с атрибути, като „реквизит“, но това е стандартната практика. Когато използваме компоненти на клас, което ще направим по-долу, един и същ списък от атрибути винаги се представя със специална собственост на екземпляра, наречена реквизити.

    Обърнете внимание, че получаването на реквизит не е задължително. Някои компоненти няма да имат реквизит. Възвръщаемостта на компонента обаче не е задължителна. Реагиращият компонент не може да върне „неопределен“ (нито изрично, нито неявно). Трябва да върне стойност. Той може да върне „null“, за да накара рендерът да игнорира резултата си.

    Обичам да използвам унищожаване на обекти винаги, когато използвам компонентни реквизити (или състояние, наистина). Например функцията на компонента на Button може да бъде написана така с деструкция на подпори:

    const Button = ({label}) => (
      
    );

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

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

    Изрази в JSX

    Можете да включите JavaScript израз, използвайки чифт къдрави скоби навсякъде в JSX:

    // jsdrops.com/bx4
    const RandomValue = () => (
      
        {Math.floor (Math.random () * 100)}    );
    ReactDOM.render (, mountNode);

    Обърнете внимание, че само тези изрази могат да бъдат включени в тези къдрави скоби. Например, не можете да включите редовен if-инструкция, но терминалният израз е добре. Всичко, което връща стойност, е наред. Винаги можете да поставите всеки код във функция, да го накарате да върне нещо и да го повикате в къдравите скоби. Обаче сведете до минимум логиката, която въвеждате в тези къдрави скоби.

    JavaScript променливите също са изрази, така че когато компонентът получи списък на реквизит, можете да използвате тези подпори в къдрави скоби. Ето как използвахме {label} в примера на бутона.

    Обектните литерали на JavaScript също са изрази. Понякога използваме JavaScript обект вътре в къдрави скоби, което го прави да изглежда като двойни къдрави скоби: {{a: 42}}. Това не е различен синтаксис; това е просто обект буквален, определен в обикновените къдрави скоби на JSX.

    Например, един случай за използване на обект буквал в тези къдрави скоби е да се предаде обект в стил CSS към атрибута на специален стил в React:

    // jsdrops.com/bx5
    const ErrorDisplay = ({съобщение}) => (
      
        {Съобщение}    );
    ReactDOM.render (
      ,
      mountNode
    );

    Атрибутът за стил по-горе е специален. Ние използваме обект като негова стойност и този обект определя стиловете, сякаш ги настройваме чрез JavaScript DOM API (имена на свойства на камила, строеви стойности). React превежда тези обекти в стил в атрибути в стил CSS. Това не е най-добрият начин за стилизиране на компонент React, но ми е изключително удобно да се използва, когато прилагате условни стилове към елементи. Например, тук е компонент, който ще изведе текста си в зелено или червено на случаен принцип около половината от времето:

    // jsdrops.com/bx6
    клас ConditionalStyle разширява React.Component {
      render () {
        връщане (
          
            Как ви харесва това?            );   } }
    ReactDOM.render (
      ,
      mountNode,
    );

    Логиката за този стайлинг е точно в компонента. Харесва ми! С това е по-лесно да се работи, отколкото условно да се използва име на клас и след това да се проследи какво прави това име на класа в глобалния CSS таблица стилове.

    JSX не е език на шаблон

    Някои библиотеки, които се занимават с HTML, предоставят шаблон на език за него. Вие пишете динамичните си изгледи с „подобрен“ HTML синтаксис, който има контури и условия. След това тези библиотеки ще използват JavaScript за преобразуване на шаблоните в DOM операции. След това DOM операциите могат да се използват в браузъра за показване на DOM дървото, описано от подобрения HTML.

    Реагира елиминира тази стъпка. Изобщо не изпращаме до браузъра шаблон с приложение React. Изпратихме му дърво от обекти, описани с API на React. React използва тези обекти за генериране на DOM операции, необходими за показване на желаното DOM дърво.

    Когато се използва HTML шаблон, библиотеката анализира приложението ви като низ. Приложението React се анализира като дърво на обекти.

    Въпреки че JSX може да изглежда като шаблон на език, той наистина не е. Това е просто разширение на JavaScript, което ни позволява да представим дървото на обектите на React със синтаксис, който прилича на HTML шаблон. Браузърите изобщо не трябва да се справят с JSX и React също не трябва да се справя с него! Само компилаторът го прави. Това, което изпращаме в браузъра, е без шаблон и код без JSX.

    Например, за масива todos, който видяхме по-горе, ако трябва да показваме този масив в потребителски интерфейс с помощта на шаблон на език, ще трябва да направим нещо като:

    <Ул>
      <% ЗА всеки тодо в списъка на todos%>
        
  • <% = todo.body%>
  •   <% КРАЙ ЗА%>
    <%%> Е един синтаксис, който представя динамичните подобрени части. Може да видите и {{}} синтаксиса. Някои езици на шаблони използват специални атрибути за тяхната подобрена логика. Някои езици на шаблоните използват отстъпи на бялото пространство (правило извън страницата).

    Когато настъпят промени в масива todos (и трябва да актуализираме това, което е представено в DOM с език на шаблон), ще трябва или да рендерираме този шаблон, или да изчислим къде в DOM дървото трябва да отразяваме промяната в масива todos ,

    В приложението React изобщо няма език на шаблона. Вместо това използваме JSX:

    <Ул>
      {todos.map (todo =>)
        
  • {todo.body}   )}
  • Който преди да бъде използван в браузъра, се превежда на:

    React.createElement (
      "Ул"
      нула,
      todos.map (todo =>)
        React.createElement ("li", null, todo.body)
      ),
    );

    React приема това дърво на обектите и го трансформира в дърво от DOM елементи. От наша гледна точка сме свършили с това дърво. Не управляваме никакви действия по него. Ние просто управляваме действията в самия масив todos.

    Създаване на компоненти с помощта на класове

    React поддържа създаването на компоненти и чрез синтаксиса на клас JavaScript. Ето същия пример за компонент на Button, написан със синтаксиса на класа:

    // jsdrops.com/bx7
    клас бутон разширява React.Component {
      render () {
        връщане (
          <Бутон> {this.props.label} 
        );
      }
    }
    // Използвайте го (същия синтаксис)
    ReactDOM.render (<етикет на бутона = "Запазване" />, mountNode);

    В този синтаксис определяте клас, който разширява React.Component, който е един от основните класове в API на най-високо ниво на React. Компонентът React, базиран на клас, трябва поне да дефинира метод на име, наречен render. Този метод на визуализация връща елемента, който представлява изхода на обект, създаден от компонента. Всеки път, когато използваме компонента, базиран на клас Button (чрез изобразяване на <Бутон ... />), React ще създаде обект от този компонент, базиран в клас, и ще използва представяне на този обект, за да създаде DOM елемент. Той също така ще асоциира DOM-рендирания елемент с екземпляра, създаден от класа.

    Обърнете внимание как използвахме this.props.label във вътрешността на визуализирания JSX. Всеки компонент получава свойство за специален екземпляр с име реквизит, което съдържа всички стойности, предадени на елемента на този компонент, когато той е бил създаден. За разлика от функционалните компоненти, функцията за изобразяване в компоненти, базирани на клас, не получава никакви аргументи.

    Функции спрямо класове

    Компоненти, създадени с функции, използвани за ограничаване в React. Единственият начин да се направи компонент „stateful“ беше да се използва синтаксисът на класа. Това се промени с пускането на „React Hooks“, започващо с React версия 16.8, която беше пусната в началото на 2019 г. Релизът React hooks въведе нов API, за да направи функционален компонент състоятелен (и да му даде много други функции).

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

    Вярвам, че новият API ще замени бавно стария, но това не е единствената причина, която искам да ви насърча да го използвате (изключително ако можете).

    Използвах и двата API в големи приложения и мога да ви кажа, че новият API е много по-превъзходен от стария по много причини, но ето тези, които аз лично смятам, че са най-важни:

    • Не е нужно да работите с „инстанции на клас“ и тяхното имплицитно състояние. Работите с прости функции, които се обновяват при всяко изобразяване. Държавата е изрично декларирана и нищо не е скрито. Всичко това основно означава, че ще срещнете по-малко изненади в своя код.
    • Можете да групирате свързана със състоянието си логика и да я разделите на самостоятелни съставни и сменяеми единици. Това улеснява разбиването на сложни компоненти на по-малки части. Освен това прави тестовите компоненти по-лесни.
    • Можете да консумирате всякакви състояния на логика по декларативен начин и без да е необходимо да използвате някакво йерархично „влагане“ в дървета на компоненти.

    Докато компонентите, базирани на класове, ще продължат да бъдат част от React в обозримо бъдеще, като новак в екосистемата мисля, че има смисъл да започнете чисто с просто функции (и куки) и да се съсредоточите върху изучаването на новия API (освен ако не трябва да работят с кодова база, която вече използва класове).

    Компоненти срещу елементи
    Можете да намерите думите „компонент“ и „елемент“, смесени в ръководствата и ръководствата на React там. Мисля, че обучаващият се в React трябва да разбере важните различия.
    Реактивен компонент е шаблон. Проект. Глобална дефиниция. Това може да бъде или функция, или клас (с метод на изобразяване).
    Реактивен елемент е това, което се връща от компоненти. Това е обект, който на практика описва DOM възлите, които даден компонент представлява. При функционален компонент този елемент е обектът, който функцията връща, а с компонент от класове елементът е обектът, който методът на изобразяване на компонента се връща. Реагиращите елементи не са това, което виждате в браузъра. Те са само обекти в паметта и не можете да промените нищо за тях.
    Реагира вътрешно създава, актуализира и унищожава обекти, за да разбере дървото на DOM елементи, което трябва да бъде предоставено на браузъра. Когато работите с компоненти на класа, обикновено е да се отнасят към DOM елементите им, предоставени от браузъра, като компоненти на компоненти. Можете да изобразите много екземпляри от един и същи компонент. Инстанцията е ключовата дума „това“, която използвате в компоненти, базирани на клас. Няма да е необходимо да създавате екземпляр от клас ръчно. Просто трябва да запомните, че той е някъде в паметта на React. За функционалните компоненти React просто използва извикването на функцията, за да определи какъв DOM елемент да представи.

    Предимства на компонентите

    Терминът „компонент“ се използва от много други рамки и библиотеки. Можем дори да пишем уеб компоненти родно чрез HTML5 функции като персонализирани елементи и HTML импортиране.

    Компонентите, независимо дали работим с тях на местно ниво или чрез библиотека като React, имат много предимства.

    Първо, компонентите правят кода ви по-четим и лесен за работа. Помислете за този потребителски интерфейс:

    
     
    

    Какво представлява този потребителски интерфейс? Ако говорите HTML, можете да го разберете бързо тук и да кажете „това е изображение с възможност за кликване.“ Ако ще превърнем този потребителски интерфейс в компонент, можем просто да го наречем ClickableImage!

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

    
      
      <Оставащи Характеристики>
      
    

    Без да разглеждаме действителния HTML код, ние знаем точно какво представлява този потребителски интерфейс. Освен това, ако трябва да променим изхода на секцията с останалите символи, знаем точно къде да отидем.

    Реагиращите компоненти също могат да бъдат използвани повторно в едно и също приложение и в множество приложения. Например, ето възможна реализация на компонента ClickableImage:

    const ClickableImage = ({href, src}) => {
      връщане (
        
          
        
      );
    };

    Наличието на променливи както за href, така и за src реквизита е това, което прави този компонент за многократна употреба. Например, за да използваме този компонент, можем да го изобразим с набор от реквизити:

    И можем да го използваме повторно, като използваме различен набор реквизити:

    Във функционалното програмиране имаме концепцията за чисти функции. Те са основно защитени от всяка външна държава; ако им дадем един и същ принос, винаги ще получаваме един и същ резултат.
    Ако компонентът на React не зависи от (или модифицира) нещо извън определението му (например, ако не използва глобална променлива), можем да маркираме и този компонент чист. Чистите компоненти имат по-голям шанс да бъдат използвани повторно без проблеми.

    Ние създаваме компоненти за представяне на изгледи. За ReactDOM компонентите React, които дефинираме, ще представляват HTML DOM възли. Компонентът ClickableImage по-горе беше съставен от два вложени HTML елемента.

    Можем да мислим за HTML елементи като вградени компоненти в браузъра. Можем да използваме и собствени персонализирани компоненти, за да съставим по-големи. Например, нека напишем компонент, който показва списък с търсачки.

    const SearchEngines = () => {
      връщане (
        
                         ); };

    Обърнете внимание как използвах компонента ClickableImage, за да съставя компонента SearchEngines!

    Можем също така да направим компонента SearchEngines за многократна употреба, като извлечем данните му в променлива и го проектираме да работи с тази променлива.

    Например, можем да въведем масив от данни във формат като:

    данни за const = [
      {href: "http://google.com", src: "google.png"},
      {href: "http://bing.com", src: "bing.png"},
      {href: "http://yahoo.com", src: "yahoo.png"}
    ];

    След това, за да работи , ние просто картографираме масива от списък от обекти в списък на компоненти на ClickableImage:

    const SearchEngines = ({двигатели}) => {
      връщане (
        <Списък>
          {Engine.map (engine => )}
        
      );
    };
    ReactDOM.render (
     ,
     document.getElementById ( "mountNode")
    );

    Този SearchEngines може да работи с всеки списък от търсачки, които му предоставяме.

    Какво точно са куки?

    Куката в компонента React е призив към специална функция. Всички функции на куките започват с думата „употреба“. Някои от тях могат да се използват за предоставяне на функционален компонент със състоятелни елементи (като useState), други могат да се използват за управлявани странични ефекти (като useEffect) или за кеширане / запомняне на функции и обекти (като useCallback). Куките са много мощни и небето е границата, когато става въпрос за неща, които можете да правите с тях.

    Функциите за реагираща кука могат да се използват само функционални компоненти. Не можете да ги използвате в компоненти на класа.

    За да видите пример на основната кука useState, нека накараме компонента на бутона по-горе да реагира на събитие с щракване. Нека поддържаме броя на кликванията в променлива „преброяване“ и показваме стойността на тази променлива като етикета на бутона, който прави.

    const Button = () => {
      нека брой = 0;
      връщане (
        <Бутон> {брой} 
      );
    };
    ReactDOM.render (<Бутон />, mountNode);

    Тази променлива ще бъде елементът на състоянието, който трябва да въведем в примера. Това е част от данните, от които ще зависи потребителският интерфейс (защото ние го показваме) и е елемент на състоянието, защото той ще се променя с течение на времето.

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

    Преди да успеем да променим стойността на състоянието на броя, трябва да научим за събитията.

    Отговаряне на потребителски събития

    Можете да добавите манипулатор на събития със свойство „onEvent“ (към елемента с бутони в този случай). Това може да бъде onClick, onMouseOver, onScroll, onSubmit и т.н.

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

    const Button = () => {
      нека брой = 0;
      връщане (
        <бутон onClick = {() => console.log ('Бутон кликна')}>
          {броя}
        
      );
    };
    ReactDOM.render (<Бутон />, mountNode);

    За разлика от DOM версията на атрибута onClick (която използва низ) атрибутът onClick на React използва справка за функция. Вие посочвате, че вътре в къдрави скоби.

    функция func () {}
    <бутон onClick = {func} />

    Обърнете внимание как ние предадохме референтната функция (име) като ръководител на onClick. Там не се позовахме на func. React ще извика функцията, когато натиснете бутона.

    За събитието onClick в компонента на бутона по-горе, ние „начертахме“ дефиниция на функция, която при извикване ще изведе съобщение към конзолата. Всеки път, когато кликнем върху бутона, обработващият onClick (функцията със стрелка вградена линия) ще бъде извикан и ще видим това съобщение.

    Обърнете внимание как името на събитието е камилско. Всички атрибути, свързани с DOM (които се обработват от React), трябва да са регистър на камилата (и React ще покаже грешка, ако това не е така). React също така поддържа използването на персонализирани HTML атрибути и те трябва да са във всички малки букви.
    Някои DOM атрибути в React са малко по-различни от тези, които правят в обикновения DOM API. Пример за това е събитието onChange. В обикновен браузър той обикновено се задейства, когато щракнете извън поле на формуляр (или раздела извън него). В React, onChange се задейства всеки път, когато стойността на полето на формуляр се промени (при всеки добавен / премахнат символ).
    Някои атрибути в React са наречени по различен начин от техния HTML еквивалент. Пример за това е атрибутът className в React, който е еквивалентен на използването на атрибута клас в HTML. За пълен списък на разликите между атрибутите React и DOM атрибутите вижте jscomplete.com/react-attributes.

    Състояние за четене и актуализиране

    За да проследява актуализациите на състоянието и да задейства виртуално DOM различаващо се и реално съвместяване на DOM, React трябва да е наясно с всички промени, които се случват с всички елементи на състоянието, които се използват в компонентите. За да направи това по ефикасен начин, React изисква използването на специални getters и setters за всеки елемент на състоянието, който въвеждате в компонент. Това е мястото, където куката useState влиза в игра. Той дефинира елемент на състоянието и ни връща геттер и сетер за него!

    Ето какво ни е необходимо за елемента на състоянието на броя, който се опитваме да приложим:

    const [count, setCount] = React.useState (0);

    Функцията useState връща масив с точно 2 елемента. Първият елемент е стойност (getter), а вторият елемент е функция (setter). Използвах унищожаване на масив, за да дам имена на тези елементи. Можете да им дадете всякакви имена, които искате, но [name, setName] е конвенцията.

    Първата стойност „стойност“ може да бъде низ, число, масив или други видове. В този случай ни трябваше число и трябваше да инициализираме това число с 0. Аргументът на React.useStateis използва като начална стойност на елемента на състоянието.

    Вторият елемент „функция“ ще промени стойността на елемента на състоянието при извикване (и ще задейства DOM обработка, ако е необходимо). Всеки път, когато се извика функцията setCount, React ще направи отново компонента на бутона, който ще опресни всички променливи, дефинирани в компонента (включително стойността на броя). Аргументът, който предаваме на setCount, става новата стойност за броене.

    Това, което трябва да направим, за да увеличим бутона на етикета му, е да извикаме функцията setCount в събитието onClick и да предадем на нея текущата стойност на броя, увеличена с 1. Ето пълния код на примера на бутона за увеличаване на етикета:

    const Button = () => {
      const [count, setCount] = useState (0);
      връщане (
        <бутон onClick = {() => setCount (брой + 1)}>
          {броя}
        
      );
    };
    ReactDOM.render (<Бутон />, mountNode);

    Върви напред и тествай това. Бутонът ще увеличава етикета си при всяко щракване.

    Обърнете внимание как не осъществихме никакви действия за промяна на самия потребителски интерфейс. Вместо това реализирахме действие за промяна на JavaScript обект (в паметта)! Нашата реализация на потребителския интерфейс беше в основата на React, че искаме етикетът на бутона винаги да отразява стойността на countobject. Кодът ни не правеше никакви актуализации на DOM Реакт го направи.

    Обърнете внимание също как използвах ключовата дума const за определяне на броя, въпреки че това е стойност, която се променя! Нашият код няма да промени тази стойност. React ще използва, когато използва ново извикване на функцията Button, за да предостави потребителския интерфейс на новото си състояние. В този нов разговор, функцията useState ще ни даде нова стойност на преброяване.

    Функцията useState е достъпна в световен мащаб на детската площадка. Това е просто псевдоним на React.useState. Във вашия код можете да използвате импортиран импорт, за да използвате useState директно в обхвата на модул:
    import React, {useState} от 'react';

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

    Работа с множество компоненти

    Нека разделим компонента на Button, който досега имаме, на два компонента:

    • Съхранявайте компонент на Button, за да представлява елемент на бутона, но със статичен етикет.
    • Добавете нов компонент на дисплея, за да покажете стойността на броя

    Новият компонент на дисплея ще бъде чисто презентационен, без състояние или взаимодействия. Това е нормално Не всеки компонент на React трябва да има страхотни куки или да бъде интерактивен.

    const Display = (реквизит) => (
      
     СТОЙНОСТ НА КОЛУКАТА ТУК ... 
    );

    Отговорността на компонента Display е просто да покаже стойност, която ще получи като опора. Например, фактът, че преди елемент е бил използван за хостинг на стойността, е част от тази отговорност. Други компоненти в това приложение не казват нищо за това!

    Осигуряване на компоненти за братя и сестри

    Вече имаме два елемента за изобразяване: Бутон и Дисплей. Не можем да ги изобразим директно един до друг по този начин:

    // Това няма да работи
    ReactDOM.render (<Бутон /> <Дисплей />, mountNode);

    Съседни елементи не могат да бъдат изобразени така в React, защото всеки от тях се превежда във функционално обаждане, когато JSX се преобразува. Имате няколко възможности да се справите с този проблем.

    Първо, можете да предадете масив от елементи на ReactDOM.render и да вмъкнете в този масив толкова много React елементи, колкото искате.

    Опция 1

    ReactDOM.render ([<Бутон />, <Дисплей />], mountNode);

    Това обикновено е добро решение, когато всички елементи, които представяте, идват от динамичен източник. Това обаче не е идеално за случая, който правим тук.

    Друга опция е да направите елементите на братята React деца на друг елемент React. Например, можем просто да ги затворим в елемент div.

    Вариант №2

    ReactDOM.render (
      
        <Бутон />     <Показване />   ,   mountNode );

    React API поддържа това влагане. Всъщност React има специален обект, ако трябва да затворите множество съседни елементи като този, без да въвеждате нов DOM родителски възел. Можете да използвате React.Fragment:

    Вариант №3

    ReactDOM.render (
      
        <Бутон />
        <Показване />
      
      mountNode
    );

    Този случай е толкова често срещан в React, че разширението JSX има пряк път за него. Вместо да пишете React.Fragment, можете просто да използвате празен маркер <> .

    Вариант №3 +

    ReactDOM.render (
      <>
        <Бутон />
        <Показване />
      
      mountNode
    );

    Празният маркер ще се преобразува в синтаксиса на React.Fragment. Ще използвам този синтаксис, за да продължа с примера.

    Въпреки това, винаги трябва да се опитвате да направите първия аргумент на ReactDOM.render едно компонентно обаждане вместо вложеното дърво, което току-що направихме. Това е по същество предпочитание за качество на кода. Принуждава ви да мислите за вашата йерархия на компоненти, имена и отношения. Нека направим това по-нататък.

    Компонентът от най-високо ниво

    Нека представим компонент от най-високо ниво, който да хоства както компонентите на бутона, така и на дисплея. Въпросът сега е: какво да наречем този нов родителски компонент?

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

    Тъй като този нов родителски компонент ще хоства дисплей с бутон, който увеличава показания брой, можем да мислим за него като мениджър на стойността на броя. Нека го наречем CountManager.

    const CountManager = () => {
      връщане (
        <>
          <Бутон />
          <Показване />
        
      );
    };
    ReactDOM.render (, mountNode);

    Тъй като ще покажем стойността на броя в новия компонент на дисплея, вече не е необходимо да показваме стойността на броя като етикет на бутона. Вместо това можем да променим етикета на нещо като „+1“.

    const Button = () => {
      връщане (
        <бутон onClick = {() => console.log ('TODO: Брояч на повишаване')}>
          1
        
      );
    };

    Обърнете внимание, че аз също премахнах елемента състояние от компонента Button, защото вече не можем да го имаме. С новото изискване и компонентите на бутона и дисплея се нуждаят от достъп до елемента на състояние на броене. Компонентът на дисплея ще го покаже, а компонентът на бутона ще го актуализира. Когато компонент трябва да получи достъп до елемент на състоянието, който е собственост на неговия компонент, един от решенията е да "повдигне" този елемент едно ниво нагоре и да го определи вътре в своя родителски компонент. За този случай родителят е компонентът CountManager, който току-що представихме.

    Преминавайки състоянието към CountManager, вече можем да "предаваме" данни от родител към дете, използвайки компонентни реквизити. Това е, което трябва да направим, за да покажем стойността на броя в компонента Display:

    const Display = ({съдържание}) => (
      <Предварително> {съдържание} 
    );
    const CountManager = () => {
      const [count, setCount] = useState (0);
      връщане (
        <>
          <Бутон />
          <Покажи съдържание = {count} />
        
      );
    };
    ReactDOM.render (, mountNode);

    Обърнете внимание как в CountManager използвах точно същата линия useState, която беше в компонента Button. Ние повдигаме същия елемент на състоянието. Също така имайте предвид, че когато насочих стойността на броя до компонента на дисплея чрез опора, използвах различно име за него (съдържание). Това е нормално Не е нужно да използвате точно същото име. В действителност, в някои случаи въвеждането на нови общи имена е по-добре за компонент за деца, защото ги прави по-многократно използваеми. Компонентът на дисплея може да бъде използван повторно за показване на други цифрови стойности освен броя.

    Компонентите на родителите могат също да поведат поведението на децата си, което е необходимо да направим по-нататък.

    Тъй като елементът състояние на преброяване сега е в компонента CountManager, ние се нуждаем от функция на това ниво, за да се справим с актуализирането му. Нека дадем име на тази функция incrementCounter. Логиката за тази функция всъщност е същата логика, която имахме и преди във функцията на двата вида в компонента Button. Новата функция incrementCounter ще актуализира състоянието на брояча на компонента CountManager, за да увеличи стойността, използвайки предишната стойност:

    const CountManager = () => {
      // ....
      const incrementCounter = () => {
        setCount (брой + 1);
      }
      // ...
    };

    Сега обработващият onClick в компонента Button трябва да се промени. Искаме тя да изпълнява функцията incrementCounter, която е в компонента CountManager, но компонентът може да има достъп само до собствените си функции. Така че, за да направим компонента Button способен да извика функцията incrementCounter в компонента CountManager, можем да предадем препратка към incrementCounter към компонента Button като опора. Да, реквизитът може да съдържа и функции, а не само данни. Функциите са само обекти в JavaScript и също като обекти можете да ги предавате.

    Можем да назовем тази нова опора каквото и да било. Ще го нарека clickAction и ще му предам стойност incrementCounter, която е препратката към функцията, която дефинирахме в компонента CountManager. Можем да използваме това ново поведение на предаване директно като стойност на onClick обработващия. Това ще бъде опора за компонента Button:

    const Button = ({clickAction}) => {
      връщане (
        <бутон onClick = {clickAction}>
          1
        
      );
    };
    // ...
    const CountManager = () => {
      // ...
      връщане (
        'DIV>
          <Бутон clickAction = {incrementCounter} />
          <Покажи съдържание = {count} />
        
      );
    };

    Тук се случва нещо много мощно. Това свойство clickAction позволи на компонента Button да извика функцията на компонента CountManager incrementCounter. Когато натискаме този бутон, компонентът Button посяга към компонента CountManager и казва: „Здравей родител, продължавай и се позовавай на това поведение на брояча на увеличение сега“.

    В действителност компонентът CountManager е този, който се контролира тук, а компонентът Button просто следва общи правила. Ако анализирате кода такъв, какъвто е сега, ще разберете как компонентът Button няма представа какво се случва при щракване. Той просто следва правилата, определени от родителя и се позовава на общо clickAction. Родителят контролира какво влиза в това родово поведение. Това следва концепцията за изолиране на отговорността. Всеки компонент тук има определени отговорности и те се фокусират върху това.

    Вижте компонента Display за друг пример. От своя гледна точка стойността на броя не е състояние. Това е просто опора, че компонентът CountManager преминава към него. Компонентът дисплей винаги ще показва тази опора. Това също е разделение на отговорностите.

    Като дизайнер на тези компоненти вие избирате нивото на своите отговорности. Например, бихме могли да поемем отговорност за показването на частта за броене на самия компонент на CountManager и да не използваме нов компонент за това.

    Компонентът CountManager отговаря за управлението на състоянието на броя. Това е важно дизайнерско решение, което взехме и ще трябва да направите много в приложението React. Къде да определим държавата?

    Практиката, която следвам, е да дефинирам елемент на състояние в споделен родителски възел, който е възможно най-близо до всички деца, които се нуждаят от достъп до този елемент на състоянието. За малко приложение като това, това обикновено означава самия компонент от най-високо ниво. В по-големи приложения, под-дърво може да управлява своя "клон" на държавата, а не да разчита на глобални елементи на състоянието, които са дефинирани в кореновия компонент от най-високо ниво.

    Компонентът от най-високо ниво се използва за управление на споделено състояние и действия на приложението, тъй като е родител на всички други компоненти. Внимавайте с този дизайн, тъй като актуализирането на елемент на състоянието на компонента от най-високо ниво означава, че цялото дърво на компонентите ще бъде рендерирано (в паметта).

    Ето пълния код за този пример досега:

    // jsdrops.com/bx8
    const Button = ({clickAction}) => {
      връщане (
        <бутон onClick = {clickAction}>
          1
        
      );
    };
    const Display = ({съдържание}) => (
      <Предварително> {съдържание} 
    );
    const CountManager = () => {
      const [count, setCount] = useState (0);
      const incrementCounter = () => {
        setCount (брой + 1);
      };
      връщане (
        
          <Бутон clickAction = {incrementCounter} />       <Покажи съдържание = {count} />        ); };

    Правене на компоненти за многократна употреба

    Компонентите са свързани с повторното използване. Нека направим компонента Button за многократна употреба, като го променим, така че да може да увеличи глобалния брой с всяка стойност, а не само с 1.

    Нека започнем с добавяне на още елементи на бутона в компонента CountManager, за да можем да тестваме тази нова функция:

    const CountManager = () => {
      // ..
      връщане (
        <>
          <Бутон clickAction = {incrementCounter} /> {/ * +1 * /}
          <Бутон clickAction = {incrementCounter} /> {/ * +5 * /}
          <Бутон clickAction = {incrementCounter} /> {/ * +10 * /}
          <Показване на броя = {брой} />
        
      );
    };

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

    Ето потребителския интерфейс, който имам предвид, след като щракнете върху всеки бутон веднъж:

    На екранната снимка по-горе, броят започна с 0. Добавих 1, след това 5 и след това 10, за да стигна до 16

    Преди да преминем през това упражнение, отделете малко време и помислете за това и се опитайте да го приложите сами. Тя е най-вече пряма. Съвет: ще трябва да въведете 1 нова опора за Button. Опитай. Върнете се, когато сте готови да сравните решението си с моето.

    Добавяне на нови реквизити

    Първото нещо, което трябва да направим, е да направим етикета +1 в компонента Button да бъде персонализиран.

    За да направим нещо приспособимо в компонента React, ние въвеждаме нова опора (която родителският компонент може да контролира) и принуждаваме компонента да използва неговата стойност. За нашия пример можем да накараме компонентът Button да получава сумата на инкремент (1, 5, 10) като нова опора. Ще го нарека clickValue. Можем да променим метода на изобразяване в CountManager, за да предадем стойностите, с които искаме да тестваме, към тази нова опора.

    връщане (
      <>
        <Бутон clickAction = {incrementCounter} clickValue = {1} />
        <Бутон clickAction = {incrementCounter} clickValue = {5} />
        <Бутон clickAction = {incrementCounter} clickValue = {10} />
        <Покажи съдържание = {count} />
      
    );

    Забележете няколко неща за този код досега:

    • Не нарекох новия имот с нищо, свързано с броя. Компонентът Button не трябва да е наясно със значението на неговото кликване. Просто трябва да предаде това clickValue, когато неговото събитие за щракване е задействано. Например, именуването на това ново свойство countValue не би било най-добрият избор, защото сега в компонента на Button четем кода, за да разберем, че елемент на Button е свързан с броя. Това прави компонента на Button по-малко използваем. Например, ако искам да използвам същия компонент на Button, за да добавя буква към низ, неговият код би бил объркващ.
    • Използвах къдрави скоби, за да предам стойностите на новото свойство clickValue (clickValue = {5}). Не използвах низове там (clickValue = "5"). Тъй като имам математическа операция, свързана с тези стойности (всеки път, когато се натисне бутон), имам нужда от тези стойности, за да бъдат числа. Ако ги предам като низове, ще трябва да направя някакво преобразуване на низове в номер, когато операцията за добавяне трябва да бъде изпълнена.
    Предаването на число като низ е често срещана грешка в React. Вижте тази статия за повече често срещани грешки, свързани с реакцията.

    Персонализиране на поведението

    Другото, което трябва да направим общо в компонента CountManager, е функцията за действие incrementCounter. Той не може да има твърдо кодиран брой + 1 операция, както сега. Подобно на това, което направихме за компонента Button, за да направим функцията обобщена, ние я караме да получава аргумент и използва стойността на този аргумент. Например:

    incrementCounter = (incrementValue) => {
      setCount (count + incrementValue);
    };

    Сега всичко, което трябва да направим, е да накараме компонента Button да използва своята опора clickValue за свой етикет и да го накараме да извика своето действие onClick с своя clickValue като аргумент.

    const Button = ({clickValue, clickAction}) => {
      връщане (
        <бутон onClick = {() => clickAction (clickValue)}>
          + {ClickValue}
        
      );
    };

    Обърнете внимание как трябваше да обвия опората на onClick с функция вградена стрелка, за да го обвържа с VVueue за натискане на бутона. Затварянето на JavaScript за тази нова стрелка ще се погрижи за това.

    Трите бутона сега трябва да се увеличават с техните три различни стойности на щракване. Можете да видите кода на този пример на jsdrops.com/bx9.

    Приемане на вход от потребителя

    Представете си, че трябва да преброим знаците, които потребител използва в текстова област, точно като туитърската форма на Twitter. С всеки символ типовете потребители трябва да актуализираме потребителския интерфейс с новия брой символи.

    Ето компонент, който показва текстови елемент за въвеждане на текста с разделител за запазване на броя символи:

    // jsdrops.com/bx10
    const CharacterCounter = () => {
      връщане (