Poznajemy React – na prostym przyładzie.

Tworzymy aplikację

Stwórzmy aplikację (frontend) – wykorzystując projekt Create React App https://github.com/facebookincubator/create-react-app

Nazwijmy naszą aplikację "XO":

npx create-react-app xo

Wykonujemy posłusznie:

cd xo
yarn start

Po chwili mamy komunikat:

Równocześnie przeglądarka otwiera się na stronie http://localhost:3000/ i widzimy efekt naszej pracy:


Co tam zostało zrobione?

1) Plik HTML: public/index.html z znacznikiem root, który wskazuje miejsce wstawienia aplikacji:

<div id="root"></div>

2) Wstawieniem aplikacji (modyfikacją DOM) zajmuje się napisany w React skrypt src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';


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

3) Sama aplikacja jest w pliku src/App.js a jej wygląd opisano w src/App.css.

React jako generator HTML

Tworząc własną aplikację zacznijmy od modyfikacji tego pliku (App.js). Będziemy tworzyć aplikację do gry w kółko i krzyżyk.

Zatem potrzebujemy tabeli 3x3:

import React, { Component } from 'react';
import './App.css';


class App extends Component {
render() {
  return (
  <table>
  <tr><td></td><td></td><td></td></tr>
  <tr><td></td><td></td><td></td></tr>
  <tr><td></td><td></td><td></td></tr>
  </table>
  );
 }
}


export default App;

Dodajmy w css (src/App.css):

td{
  width: 60px;
  height:60px;
  border: 1px solid green;
}

Mimo, że działa - debugger nakazuje dodanie <tbody>

Mamy pierwszą korzyść w stosunku do prostego stosowania HTML: pilnowanie składni.

Uwaga:
W programach przykładowych często stosowane są konstrukcje wprowadzone do JavaScript w ES2015. Na przykład możemy uprościć konstrukcję App, stosując zapis funkcji () =>:

const App = () => {
return (
..

Ma to szczególne znaczenie w prostych funkcjach typu lambda (zob. opis Redux na końcu rozdziału).

Własności

Obiekt React (Component) zawiera pole (własność) props, w którym przechowuje własności definiowanego znacznika. Pokażemu dodawanie własności o nazwie „klasa”.

App.js:

const App = (props) => {
return (
<table className={props.klasa}>
.

index.js:

ReactDOM.render(<App klasa="plansza"/>, document.getElementById('root'))

Widzimy, że dodając w index.js własność "klasa" możemy użyć jej odwołując się poprzez: props.klasa.Zwróćmy uwagę, że użyto identyfikatora className a nie class. Możemy wstawić class (i to nawet zadziała), ale debugger nakazuje zmianę class na className. Nie piszemy HTML'a ale opisujemy jak go wygenerować! React powiela własności html, ale zmienia ich nazwy.

Najistotniejsze spośród zmian identyfikatorów wprowadzonych przez React:

  1. Użycie className zamiast class ("class" jest zarezerwowane w JavaScript).
  2. ??
  3. ??

Popawność definicji możemy sprawdzić zmieniając CSS (App.css):

.plansza {
  background-color: yellow;
  border-spacing: 0px;
  border-collapse: separate;
}

Własne znaczniki

Zapis App analogicznie jak znaczników HTML nie jest przypadkiem. Wszystkie obiekty React można traktować jak rozszerzenie struktury strony (własne drzewo takie jak DOM).
Stwórzmy zatem klasę XO (nazwa musi zaczynać się dużą literą) i zamieniamy znaczniki <td> na XO.

Plik xo.js:

import React from 'react';
class XO extends React.Component {
  render() {
    return <td></td>
  }
}

export default XO;

Plik App.js:

import XO from './xo.js';
.
<tr>
<XO></XO>

Po zainstalowaniu odpowiedniego dodatku do Google Chrome, możemy dokonać „inspekcji” tworzonej strony:

Dynamika zmian - state

Własności (props) nie są zmieniane inaczej niż w trakcie renderowania strony. Zmiany (ponowne renderowanie) jest inicjowane poprzez zmianę stanu - pola state. Wartości z tego pola mogą być użyte w renderowaniu.

Dodajemy zatem state, zakładając, że zmiany nastąpią po kliknięciu (zdarzenie onClick):

import React from 'react';

class XO extends React.Component {

  constructor(props) {
    super(props);
    this.state = {
      content: '?'
    };
  }

  onClick() {
    console.log('zmiana');
    alert('tu będzie zmiana');
  }

  render() {
    return <td onClick={this.onClick}>{this.state.content}</td>
  }
}

export default XO;

Inicjujemy stan (state) w konstruktorze - wstawiając jedno pole content.

Widzimy, że zastosowano tu konwencję znaną z różnych implementacji szablonów HTML. Kod JSX jest zapisywany w generowanej treści w nawiasach klamrowych {}.

Implementujemy funkcję „zmiana” - która ma zmieniać stan naszej aplikacji.

1) Definicja:

zmiana(xo) {
  if (xo==' ') return 'x';
  else if (xo=='x') return 'o';
  else return ' ';
}

2) Użycie: w miejsce „alert” - zdefiniowanie funkcji zmiany z wykorzystaniem zdefiniowanej funkcji..

this.setState( // spowoduje ponowny render()
  (prevState, props) => ({
     content: this.zmiana(prevState.content)
}));

Warto zwrócić uwagę na to, że w miejsce parametru podajemy funkcję, która zwraca nowy stan. To częsta konstrukcja w React (i generalnie w JavaScript) - użycie definicji funkcji tam gdzie w tradycyjnym programowaniu pojawia się odwołanie do obiektu.

3) Powiązanie funkcji onClick z kontekstem jej wykonania. Po modyfikacjach w funkcji odwołujemy się do zmiennej obiektowej this. To wymaga przekazania (bind) zmiennej dla właściwego obiektu. Możemy we własności – ale lepiej w konstruktorze. Obiektu (dodajemy na końcu konstruktora)

this.onClick = this.onClick.bind(this);

Bez tego TypeError: Cannot read property 'setState' of undefined

Trochę to dziwne, że konstruktor nie wykonuje tego domyślnie, ale to chyba zaszłość historyczna (można definiować wykonywane funkcje inaczej niż jako metodę tego obiektu).

Uwaga!

Zmieniamy tylko state a nie props. Jeśli w wartości właściwości abc ustawimy {this.state.element}, to props.abc będzie miało wartość taką jak this.state.element.

Jak to opublikować?

Przerywamy yarn i wykonujemyyarn build – w katalogu build otrzymujemy naszą stronę.

Ponowne uruchomienie serwera deweloperrskiego (localhost:3000):

yarn run start

Dalsza część aplikacji zostanie opisana w rozdziale dotycącym Redux'a.

React-native

Na bazie technologii React powstał projekt React-native, który umożliwia programowanie cross-platform. Czyli można tworzyć aplikacje "natywne" (uruchamiane nie w przeglądarce, ale w samym urządzeniu). Więcej informacji: http://maciejgos.com/programowanie-cross-platform-w-react-native/