Część I. Wprowadzenie

React to jedna z najpopularniejszych technologii używanych przez programistów frontendu.

Jest to też najbardziej lubiana tchnologia w kategorii „framework i biblioteki” (na podstawie

https://insights.stackoverflow.com/survey/2017):

Czym jest React?

React.js pozwala składać interfejs użytkownika z „komponentów”. Te komponenty mogą być łączone i zagnieżdżane. Komponenty React są równoważne widokom w MVC. Można je także traktować jako mały system, który działa samodzielnie, ma swój własny stan i przetwarza wejście na wyjście1.

Wejściem są własności (properties), które są uogólnieniem własności HTML. Wyjście to dane służące do renderowania strony. Własności są używane tak jak własności znaczników HTML – nie są zmieniane p wyrenderowaniu. Za dynamikę zmian odpowiada stan, który może być zawary w każdym z elementów (state).

React stworzono zgodnie z ideą programowania deklaratywnego. Zazwyczaj programowanie w JavaScript ma charakter imperatywny. Czyli programista opisuje szczegółowo – jak i co program ma wykonać. Jquery ułatwiał jedynie operowanie na drzewie DOM. Wzorowany na tej bibliotece AngularJS wprowadził możliwość definiowania sposobu działania poprzez własne znaczniki HTML (dyrektywy). Był to początek trendu rozwoju programowania deklaratywnego. Opisujemy, co ma być zrobione, a nie jak (to zawiera biblioteka). Szczegółowy opis w Javascript ma dotyczyć algorytmu (logiki biznesowej), a nie jego implementacji w środowisku przeglądarki. W React uzyskujemy możliwość opisu poprzez komponenty i ich własności.

React to biblioteka Javascript

React.js nie jest frameworkiem, tylko biblioteką. Obsługuje tylko interfejs użytkownika (widok). Aby zapewnić kompletne rozwiązanie, musi być łączony z innymi bibliotekami, takimi jak Redux, React-router itp.

O tym, że React jest zwykłą biblioteką Javascript możemy się przekonać, tworząc komponent w sposób opisany na stronie: https://reactjs.org/docs/react-api.html

var React = require("react");
// React component
// React.createElement(type, [props], [...children])
var reactComponent = React.createElement('div', null, 'Hello World');

Pierwszy parametr funkcji createElement to rodzaj elementu (typ elementu w drzewie Virtual DOM), a ostatni – potomek, którym może być zwykły łańcuch znaków umieszczany między znacznikami (w tym przykładzie <div></div>).

Powyższy skrypt możemy uruchomić w środowisku NodeJS po zainstalowaniu biblioteki React:

yarn init
yarn add react

Po uruchomieniu konsoli node wykonujemy test:

$ node
> var React = require("react");
> var reactComponent = React.createElement('div', null, 'Hello World');
> console.log(reactComponent);
{ '$$typeof': Symbol(react.element),
type: 'div',
key: null,
ref: null,
props: { children: 'Hello World' },
_owner: null,
_store: {} }
>

Widzimy, że utworzono obiekt typu (type) z własnościami (props):

{ children: 'Hello World' }

W katalogu console na stronie https://github.com/tenarjw/react_edu znajdziesz wiele przykładów do ćwiczeń z React z poziomu konsoli.

Komponenty. Renderowanie HTML.

Niestety obiekt React (element) ten nie zawiera funkcji wygenerowania (render) HTML’a. Musimy do tego wykorzystać drugą bibliotekę: react-dom i umieścić całość na stronie internetowej.

Linki do tych dwóch bibliotek można znaleźć na stronie: https://reactjs.org/docs/cdn-links.html.

Przykład:

<html>
<head>
 <script 
  src="https://cdnjs.cloudflare.com/ajax/libs/react/16.4.0/umd/react.production.min.js">
 </script>
 <script src=
 "https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.4.0/umd/react-dom.production.min.js">
 </script>
</head>
<body>
 <div id="root"></div>
 <script>
   var element=React.createElement('div', null, 'Hello World!');
   var div=document.getElementById('root');
   ReactDOM.render(element,div);
 </script>
</body>
</html>

Nie ma tu żadnej magii. W elemencie drzewa DOM (HTML) jest renderowany element wygenerowany przez bibliotekę React. Służy do tego funkcja ReactDOM.render. W powyższym przykładzie renderowany element to zwykły tekst.

Zobacz przykłady: t01.html, 001 na stackblitz.com

W dalszej części skryptu przykłady umieszczone na stronie stackblitz będą znaczone numerem w nawiasach klamrowych (np. [001]).

Zamiast elementu generowanego przez React możemy podać funkcję zwracającą taki element:

function App() {
  return React.createElement('div', null, 'Hello World');;
}
ReactDOM.render(App(), document.getElementById('root'))

Zobacz przykład [002]

Drzewo elementów

Funkcja ReactDOM.render wymaga dwóch parametrów: generatora elementu lub drzewa elementów React i wskazania węzła drzewa DOM w którym ma nastąpić renderowanie.

Drzewo elementów możemy uzyskać dzięki temu, że pierwszym parametrem funkcji generującej element React (React.createElement) może być element React.

function Hello() {
  return React.createElement(
    "div",
    null,
    "Hello World"
);
}

function App() {
  return React.createElement(Hello, null);
}

ReactDOM.render(App(), document.getElementById('root'))

zobacz przykład [003]

W powyższym przykładzie mamy drzewo z dwóch elementów App i Hello.

Przekazywanie własności

Funkcja generatora elementów może mieć zdefiniowany parametr (zwyczajowo nazywany props), który służy do przekazywania danych między elementami. Danych te - odpowiednik własności znaczników w HTML - są definiowane jako środkowy parametr funkcji createElement. We wcześniejszych przykładach był on wartością pustą (null):

function Hello(props) {
  return React.createElement(
   "span",
   null,
   props.text
  );
}

function App() {
  return React.createElement(Hello, { text: "Witaj świecie!" });
}

ReactDOM.render(App(), document.getElementById('root'))

zobacz przykład [004]

Widzimy, w miejsce standardowych znaczników html (jak we wcześniejszym przykładzie div), możemy użyć generatora elementów React (tu: Hello). Do tych generatorów przekazywane są własności (tu: { text: "Witaj świecie!" }).

Tak naprawdę zarówno nowe elementy, jak i elementy wyglądające na HTML są takimi samymi elementami React. W bibliotece React stworzono elementy odpowiadające podstawowym znacznikom HTML. Możemy się o tym przekonać, przekazuąc tak jak powyżej własności do znacznika input.

function App() {
  return React.createElement('input', 
   { type: 'button', value: "Kliknij mnie!" });
}

ReactDOM.render(App(), document.getElementById('root'))

Ptrzymamy przycisk z napisem "Kliknij mnie".

Zobacz przykład [005]

JSX

Prawdziwą siłę React uzyskuje dzięki zastosowaniu notacji JSX, która umożliwia zapis komponentów i przekazywanych do nich własności w postaci XML (rozszerzenia HTML). Możemy to przetestować, umieszczając nasz przykład na stronie CodePen, CodeSandbox lub stackblitz.com. W niniejszym podręczniku korzystamy z tej trzeciej opcji

Zasadniczą różnicę zobaczymy już przy modyfikacji powyższego prostego przykładu (001).

import React from 'react';
import ReactDOM from 'react-dom';

var element=<div>Hello World!!!</div>;
var div=document.getElementById('root');
ReactDOM.render(element,div);

Zobacz przykłady [006], t02.html

Widzimy, że do zmiennej element wstawiamy coś, co nie wygląda na część programu Javascript, ale fragment HTML! A jednak JSX analizuje ten zapis i przekształca go na kod Javascript! Uzyskujemy w ten sposób nie tylko rozszerzenie składni, ale też możliwość analizy kodu i usuwanie błędów pseudo-HTML w trakcie testowania.

Własne znaczniki

Generator elementów React może zwracać element JSX. Natomiast nazwą funkcji generatora możemy posługiwać się w JSX tak jak nazwą znacznika HTML. Podobnie też wygląda przekazywanie własności:

const Hello = (props) => {
return <span>Witaj {props.text}!!</span>
}

const App = () => (
<div><Hello text="ŚWIAT"/></div>
);

ReactDOM.render(<App/>, document.getElementById('root'));

Powinniśmy uzyskać napis Witaj ŚWIAT!!

Zobacz przykład [007], t03.html

Komponenty

W miejsce funkcji generatora możemy użyć obiektu klasy pochodnej względem React.Component:

class Hello extends React.Component {
  render() {
   return <span>Witaj {this.props.text}!!</span>
  }
}

const App = () => (
<div><Hello text="ŚWIAT"/></div>
);

ReactDOM.render(<App/>, document.getElementById('root'));;

Zobacz przykład [008]

Komponent musi być wyposażony w funkcję render() - zwracającą drzewo JSX. Zauważ, że w tym przypadku przekazywane własności są poami obiektu i odwołujemy sie do nich poprzez this.

Należy w tym miejscu zwrócić uwagę na to, że element JSX musi się zawierać w pojedynczych znacznikach:

źle:

render () {return(<div>a</div><div>b</div> ) }

dobrze (jeden element „opakowania”):

render () {return( <div><div>a</div><div>b</div></div> ) }

React i Javascript ES6

W powyższych przykładach użyto definicji funkcji (funkcje strzałkowe) i klas zgodnie ze standardem Javascript ES6. React wykorzystuje ten standard. Dzięki temu możemy znacząco uprościć "Reactowe" programy.

Jednym z takich stosowanych uproszczeń jest możliwość pominięcia słowa return w funkcjach które nie zawierają żadnych innych instrukcji:

class Hello extends React.Component {
  render = () =>
    <span>Witaj {this.props.text}!!</span>
}

Zobacz przykład [009]

Jest to inny zapis tej samej funkcji, którą zdefiniowano w poprzednim przykładzie (008)

Zmienne i wyrażenia w JSX

Wzorce łańcuchów znaków w JSX umieszcza się w nawiasach {}. W środku mogą pojawić się zmienne i wyrażenia – w tym odwołanie do własności (this.props.nazwa_własności).

render () {
  const name ='Jan';
  return (<div><h1>Witaj{name}</h1></div>)
}

Dotyczy to także własności:

render () {
  const className ='header';
  return(<div className={className}><h1>React</h1></div>)
}

Zobacz przykład [010], t04.html, t05.html

Zauważmy, że pojawił się tu zapis „className”, a nie classjak w HTML. Wynika to z faktu, żeclass jest słowem kluczowym. Dlatego zmieniono:

  • for → htmlFor
  • class → className
  • wszystkie atrybuty styli są zapisywane w konwencji camelCase (na przykład backgroundColor)

Więcej: https://reactjs.org/docs/introducing-jsx.html

Virtual DOM

Elementy React tworzą Virtual DOM. Jest to abstrakcyjne drzewo obiektów – takich jak HTML DOM, które nie jest zależne od konkretnej przeglądarki. Elementy drzewa są pomyślane tak, by maksymalnie uprościć i ułatwić ich używanie w dynamicznych aplikacjach. Virtual DOM jest wzorowany na DOM i dlatego JSX wygląda prawie jak czysty HTML.

React tylko zapisuje uaktualnienia zmian do DOM, ale nigdy nie czyta z niego! Wszystkie odczyty są z VirtualDOM. Dlatego jest to rozwiązanie w pełni niezależne od platformy.

Niemożliwe jest utrzymanie prawdziwych manipulacji DOM zsynchronizowanych z wirtualnym DOM React. Z tego powodu wszystkie funkcje jQuery zostały ponownie zaimplementowane w React. Używanie jQuery razem z React - choć możliwe, nie jest zalecane.

Więcej: