Ich nehme derzeit an einer Akademie zum Java Fullstack Software Engineer teil. In den kommenden Wochen möchte ich hier meine Mitschrift, so gut es geht, aufzeichnen und mitteilen. Hier ist das, was ich vom fünften Tag (mit etwas Verspätung) in Block 10 gelernt und behalten habe:
In React wollen wir Daten von einem Objekt zum anderen übertragen. Die Objekte in einer HTML-Seite sind hierarchisch angeordnet. Ein Objekt kann Daten an den Reducer senden. Der leitet sie (reduziert) weiter an einen Store.
Objekte, die sich an dem Store registriert haben, erhalten ein Update für ihre Daten, sobald sich dort etwas ändert.
Siehe auch: https://react-redux.js.org/
Input und Output müssen sich am Reducer registrieren. Nachdem der Client seine Nachrichten gesendet hat, fängt eine Funktionalität an zu arbeiten. Diese Logik beeinflusst den Store. Der Store erkennt die Änderungen und informiert die Mitglieder der Liste. Daraufhin ändern sich die States der Empfänger.
Beispiel: \Block10-React\day05\my-redux-app:
Die Konstante counterreducer ist ein eine Methode, wo im ersten Teil die Daten übergeben werden und im zweiten Teil die Logik steht. Hier als unvollständiges Beispiel, wo State auch ein Objekt (Radius,…) sein könnte:
In Zeile 14 wird der Store mit dem Reducer kombiniert. Store weiß dann, was er zu verwalten hat. Im Store wird das State gehalten:
Hier noch der Inhalt von index.js und reducer.js:
Store erbt von Object und hat einige Default-Funktionen intus. Z.B. Store.subscribe,…
Registrierung des Render-Objektes:
Zeile 50-53 erzeugt eine Haupt-Komponente, speichert sie in einer Konstanten "render" und meldet sich als Subscriber am Store (Reduzer) an.
Wir erzeugen eine Referenz-Variable und können sie dann registrieren.
Im klassischen Weg wird das Rendern direkt ausgeführt (Kommentar Zeile 57). Jetzt (Zeile 51) wird das Ausführen einer Konstante übergeben (Hier jetzt "builder").
In Zeile 52 wird diese Konstante (welches eine Funktion ist) ausgeführt und in 53 wird diese Konstante an den Store übergeben.
Die Funktionen increment und decrement führen im Store die entsprechenden Aktionen aus:
Wir installieren das Redux DevTool im Chrome-Browser
https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en
Wir können nun im Log (Strg-Shift-I) rechts oben auf den Redux-Button klicken und sehen einige Informationen mehr:
Kap1601-flux-redux
Wir kopieren unser Template in ein neues ueb-ajax Projekt. Wir benennen das Projekt im package.json in "ueb-ajax" um (Zeile 2):
Hier der funktionierrende Code von app.jsx:
import logo from './logo.svg'; import './App.css'; import React from 'react';class App extends React.Component {
constructor(props) {
// Private PropertieInformation
// Data / Model
super(props);// Private StateInformation // Data / Model this.state = { "url" : 'https://swapi.dev/api/planets/1/', "status" : '', "data" : '' }; // Databinding an die private States binden // ReadOnly auf die States für die angegebene Methode aufheben this.handlerButtonFetch = this.handlerButtonFetch.bind(this); this.handlerButtonClear = this.handlerButtonClear.bind(this);}
// Service
getDataXHR() {
console.log("Call: getDataXHR");
// 1. Local XHR erzeugen
let xhr = new XMLHttpRequest(); // ab ECMA-5.1
// 2. Init / Config des XHR-Objektes
xhr.open("GET", this.state.url, true);
// 3. Call-Back-Funktion einbinden als Lambda-Ausdrücke
xhr.onloadstart = () => {
console.log("xhr.onloadstart");
this.setState({ "status": "Load" });
};
xhr.onloadend = () => {
console.log("xhr.onloadend");
console.log("Data: " + xhr.responseText);
this.setState({ "status": "Loadend" });
this.setState({ data: xhr.responseText });
};
xhr.onprogress = () => {
console.log("xhr.onprogress");
this.setState({ "status": "OnProgress" });
;
};
// 4. Senden
xhr.send(); // bei GET xhr.send(); bei POST xhr.send(data); -> data in den RequestBody
}
// Control
handlerButtonClear(event){
this.setState({status : "Clean"});
this.setState({data : "Leer"});
}// Control handlerButtonFetch(event){ this.setState({status : "Fetch"}); this.getDataXHR(); }render() {
return (
<div className="App">
<h1>AJAX-SWAPI</h1>
<hr/>
<div className="App">
<h1>Starwars-API</h1>
<hr/>
<button onClick={this.handlerButtonFetch}>Fetch Data</button>
<button onClick={this.handlerButtonClear}>Clear</button>
<hr/>
<div >
<div>URL: {this.state.url} </div>
<div>Status: {this.state.status} </div>
<div>Data: {this.state.data} </div>
</div>
</div>
</div>);}
}
export default App;
Fetch ist ein alternativer Ansatz zu xhr.
Ein Promise ist das Versprechen, dass es irgendwann erfüllt wird. Es gibt drei Zustände: Erfüllt, Abgelehnt, ausstehend.
Das Fetch basiert auf einem Promise. Fetch macht eine Pipeline über AJAX-Antworten.
Wenn es gut geht, wird das Ergebnis in einen JSON-Datenstrom umgewandelt, danach kommt der nächste Befehl. Nach dem Komma werden die Fehlerfälle bearbeitet:
Fetch geht auch mit POST:
Xhr funktioniert immer noch, aber fetch ist moderner und kürzer.
Hier das funktionierende Katzen-Api-Beispiel-Script:
App.jsx:
import React from 'react';class App extends React.Component {
constructor(props) {
super(props);
this.state = {
breed: 'abys',
error: null,
isLoaded: false,
breedList: [],
data: []
};
this.handleGET = this.handleGET.bind(this);
this.handleChangeBreed = this.handleChangeBreed.bind(this);
}componentDidMount() { this.handleBreeds() } handleChangeBreed(event) { this.setState({ breed: event.target.value }); } handleBreeds() { fetch("https://api.thecatapi.com/v1/breeds") .then(res => res.json()) .then( (result) => { this.setState({ isLoaded: true, breedList: result }) }, (error) => { this.setState({ isLoaded: true, error }); } ) } handleGET() { console.log("Call handleGET") fetch("http://api.thecatapi.com/v1/images/search?breed_ids=" + this.state.breed) .then(res => res.json()) .then( (result) => { this.setState({ data: result }) }, (error) => { this.setState({ error }); } ) } render() { const { error, isLoaded, data } = this.state; if (this.state.isLoaded) { return ( <div> <h1>Katzen Rassen:</h1><br /> <button onClick={this.handleGET} className="btn btn-primary">Katze!</button> <select value={this.state.breed} onChange={this.handleChangeBreed}> {this.state.breedList.map(b => (<option value={b.id}>{b.name}</option>))} </select> <hr /> {data.map(d => (<div> <ul> {d.breeds.map(breed => (<div> <li>Zucht: {breed.name}</li> <li>Alternative Bezeichnungen: {breed.alt_names}</li> <li>Beschreibung: <p>{breed.description}</p> </li> <li>Lebenserwartung: {breed.life_span}</li> <li>Herkunft: {breed.origin}</li> <li>Wikipedia Link: <a href={breed.wikipedia_url} >{breed.wikipedia_url}</a></li> <li>Bild:<br /> <img alt={d.name} height={d.height * 0.3} width={d.width * 0.3} src={d.url}></img> </li> </div>))} </ul> </div> ))} </div>); } else { return ( <div> <h1>Lade...</h1> </div>); } }}
export default App;
Wir wollen, basierend auf dem Katzen-API Script-Beispiel, unsere KUG-Buch Datenbank an ein React-Script anschließen. (Nach einer Woche mit morgens 2 h ausprobieren hat es dann auch endlich geklappt):
Hier das funktionierende Script von App.jsx:
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
book: '',
error: null,
isLoaded: false,
bookList: [],
data: []
};
this.handleGET = this.handleGET.bind(this);
this.handleChangeBook = this.handleChangeBook.bind(this);
}
componentDidMount() { //Zugriff auf die Methode geben
this.handleBooks()
}
handleChangeBook(event) { //Zugriff auf die Methode geben
this.setState({ book: event.target.value });
}
handleBooks() { //Daten holen
fetch("http://localhost:8099/kugcontroller/buecherliste")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
bookList: result //Das komplette zurückgelieferte JSON-Objekt wird in bookList gespeichert
})
},
(error) => {
this.setState({
isLoaded: true,
error
});
}
)
}
handleGET() { //liest den Inhalt aus dem Auswahl-Feld aus und schreibt die gewünschten Daten in den State
var booklist = (this.state.bookList); //Wir schreiben den erhaltenen JSON-String in eine lokale Variable
// um besser an die Elemente zu kommen
console.log("booklist = " + JSON.stringify(booklist));
console.log(this.state.bookList[0]); // Das hier geht
console.log("BookList[0] = " + this.state.bookList[0]); //Das hier nicht
console.log("BookList[0] = " + JSON.stringify(this.state.bookList[0])); //Das geht wieder
var myinput = document.getElementById('mySelect'); // Inhalt aus mySelect-Button in myInput schreiben
console.log("value = " + ReactDOM.findDOMNode(myinput).value); //Das ist die ISBN-Nummer aus dem im Feld "mySelect" ausgewählten Eintrag
var isbn = ReactDOM.findDOMNode(myinput).value;
booklist.forEach(element => { // Nun gehen wir durch alle Bücher in der Liste durch ...
console.log("Element = " + JSON.stringify(element));
if (element.isbn === isbn) { //... und suchen das Element (Buch) mit der gemerkten ISBN-Nummer
console.log("Element.isbn = " + element.isbn);
console.log("Elemen.titel = " + element.titel);
this.setState({ selectedTitel: element.titel }) // Wir merken uns im State den Titel, autor, jahr und preis:
this.setState({ selectedAutor: element.autor })
this.setState({ selectedJahr: element.jahr })
this.setState({ selectedPreis: element.preis })
}
});
this.setState({ selectedISBN: isbn }); //Wir merken uns im State die ausgesuchte ISBN-Nummer
}
// So sieht das Katzenbeispiel aus:
/*
handleGET() {
console.log("Call handleGET")
fetch("http://api.thecatapi.com/v1/images/search?breed_ids=" + this.state.breed)
.then(res => res.json())
.then(
(result) => {
this.setState({
data: result
})
},
(error) => {
this.setState({
error
});
}
)
}
*/
render() {
const { error, isLoaded, data } = this.state; // der komplette State wird in data gespeichert
if (this.state.isLoaded) {
return (
<div>
<h1>Kug-Buch:</h1><br />
<button onClick={this.handleGET} className="btn btn-primary">Buch!</button>
<select id="mySelect" value={this.state.book} onChange={this.handleChangeBook}>
{this.state.bookList.map(b => (<option value={b.isbn}>{b.titel}</option>))}
</select>
<hr />
{data.map(d => (<div>
<ul>
{d.bookList.map(book => (<div>
<li>Titel: {book.titel}</li>
</div>))}
</ul>
</div>
))}
{console.log("SelectedISBN = " + this.state.selectedISBN)}
<div>
<hr />
<h3>Buchinhalt:</h3>
BuchISBN = {this.state.selectedISBN} <br />
Buchtitel = {this.state.selectedTitel} <br />
BuchAutor = {this.state.selectedAutor} <br />
BuchJahr = {this.state.selectedJahr} <br />
BuchPreis = {this.state.selectedPreis} <br />
</div>
<hr />
ISBN-Nummer vom ersten Buch:
{this.state.bookList[0].isbn} <br />
<h3>Bücherliste:</h3>
{
this.state.bookList.map(book => (<div>
Buchname: {book.titel}
</div>))
}
</div>);
}
else {
return (
<div>
<h1>Lade...</h1>
</div>);
}
}
}
export default App;
------------------------------------------------------------------------------------------ Block 10 - React ------------------------------------------------------------------------------------------Bücher:
node -v Version
npm -v Version
npm ls -g Globes Installationsverzeichnis für die NodeJS - Module : C:\Users\joachim\AppData\Roaming\npm
User-Home-Verz:\nodejs-14.17.npmrc
npm config set proxy http://username:password@host:port
npm config set https-proxy http://username:password@host:port
npm config set proxy http://...:8080/
npm config set https-proxy http://...:8080/
Entwicklungsumgebung einrichten
npm install -g create-react-app
npm ls -g
Installation react, react-dom, und react-scripts mit cra-template...:
npx create-react-app my-app01
Installing react, react-dom, and react-scripts with cra-template...
npm start Starts the development server.
npm run build Bundles the app into static files for production.
npm test Starts the test runner.
npm run eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back!
cd my-app01
npm start
> [email protected] start
> react-scripts start
....
Starting the development server...
Compiled successfully!
You can now view my-app01 in the browser.
Local: http://localhost:3000
On Your Network: http://10.0.1.238:3000
Browser http://localhost:3000/
tree /F
C:\...\day01\my-app
│ .gitignore
│ package-lock.json
│ package.json // -> pom.xml
│ README.md
│
├───node_modules // Maven
│
├───public // Static Web
│ favicon.ico
│ index.html // Single-Page -> DOM
│ logo192.png
│ logo512.png
│ manifest.json
│ robots.txt
│
└───src // Dynamic Web
App.css // App Componenten StyleSheet (View)
App.js // App Componenten Logik (Control /Model)
App.test.js // App Componenten Test
index.css
index.js // Verknüpfung Static Web mit Dynamic Web
logo.svg
reportWebVitals.js
setupTests.js
public -> index.html <=> src -> index.js
Static Dynamic
<div id="root"></div> ReactDOM.render("HTML <App/>",document.getElementById('root')
src -> App.js (Funktionale Componente)
-------------------------------------------------------
function App() {
return (
.......... "HTML" .............
);
}
export default App;
Global (C:\Users\joachim\AppData\Roaming\npm)
npm install -g ...................
npm install -g create-react-app
npm ls -g
C:\Users\joachim\AppData\Roaming\npm
├── @angular/[email protected]
├── @popperjs/[email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected]
└── [email protected]
Local (Projekt-Verz./node_modules)
npx create-react-app my-app01
npm install ...................
npm ls
Zusätzliche Module lokal installieren
npm install react-router-dom (kap14xx)
npm install react-redux (kap15xx)
npm install react-addons-css-transition-group (kap17xx)
[email protected] C:\..........\block10-react\day01\my-app01 (node_modules)
├── @testing-library/[email protected]
├── @testing-library/[email protected]
├── @testing-library/[email protected]
├── [email protected] (Shadow-Tree)
├── [email protected]
├── [email protected]
├── [email protected]
├── [email protected] (CORE-Funktionalität)
└── [email protected]
REACT sucht vom Projekt ausgehend in der Hierachie nach Open (-> C:) einen node_modules-Ordener und benutzt den ersten den er findet um zu starten
Ab [email protected]
VOR REACT 17.0.0 muste man SymLinks einrichten
Symbolistic-Links: (aus Projekt-Verzeichniss)
Linux / Mac / Unix
ln -s ../node_modules ./node_modules
Quelle Ziel
Windows
mklink /J node_modules "../node_modules"
mklink /J node_modules "../day01/node_modules"
mklink /J node_modules "../../node_modules"
Ziel Quelle
Functional Components (function NAME -> return(... React-HTML ...))
-> Stateless component
-> Small Lifecycle
Class Component (class NAME -> render(return (... React-HTML ...) ))
-> Stateful component
-> Big Lifecycle
Dateiendungen:
.ts -> *.js Funtional ES-5 function App() {
return ( "HTML");
}
.tsx -> *.jsx Klassesn ES-6 class App extends React.Component {
render() {
return ( "HTML");
}
} export default App;
jsx benötigt eine Transcription Babel d.h. erneutes übersetzen!!!!
Func-Class-Componente
//Ausgabe
render() {
return(
<p className="somevalue">This is the content!!!</p>
- <p data-myattribute="somevalue">This is the content!!!</p>
- ExpressionLanguage (AusdrucksSprache)
{..... Ausdruck .....}
<h1>{1 + 1}</h1>
<h1>{i === 1 ? 'True!' : 'False'}</h1>
<h1 style = {myStyle}>Header</h1>
)
}
{} -> Natives JavaScript
{ /*Multi line comment...*/ }
// JS-Kommentar
<!-- HTML-Kommentar -->
HTML tags always use lowercase tag names, while React components start with Uppercase.
Note: You should use className and htmlFor as XML attribute names instead of class and for.
class -> className
for -> htmlFor (<lable>)
Componenten Eigenschaften / Parameter
HTML
------
<App name="Homer", age ="57"/>
Componente
-------------------
class App extends React.Component {
constructor(props) {
super(props);
console.log("Show Name-Property:" + this.props.name); // Show Name-Property; Homer
}
State -> Eigenschaften innerhalb einer Componente um StatusInformationen zu speichern
this.state
constructor() {
// Private StateInformation
// Data / Model
this.state = {
"name" : '',
"gebjahr" : '',
"alter" : '',
"show" : false,
"myStyle" : {display:'none'}
};
}
...
// this.state.name = event.target.value; // So nicht da this.state.name da KEIN Access erlaubt ist ohne Bindung
this.setState({name : event.target.value}); // setState() -> Observable ( this.state) -> render(){}
Berechnung augelagert (Funktion oder Methode)
From: input: Zahl
button: Berechnung Aufräumen
Out: Zahl , Kehrwert
Fehlermeldung
Funct-Componenten -> HOCs (Higher Order Components)
Class-Componenten -> component...... - Methoden
clearInput() {
this.setState({ data: '' });
//Alternative
var myinput = document.getElementById('myinput');
ReactDOM.findDOMNode(myinput).focus();
// Reference Deprecated
ReactDOM.findDOMNode(this.refs.myInput).focus();
}
render() {
return (
<div>
<input id='myinput' value={this.state.data} onChange={this.updateState}
ref="myInput"></input>
<button onClick={this.clearInput}>CLEAR</button>
<h4>{this.state.data}</h4>
</div>
);
}
Collection [][][][][][] -> React-DOM-Baum-Elementen (<li>, <tr>, <div>,....)
JavaScript React
{this.state.data.map((dynamicComponent, i) =>
<Content key={i} componentData={dynamicComponent} />)} // n - Content-Elemente
index.js:1 Warning: Each child in a list should have a unique "key" prop.
Aufteilung unserer Browser-Darstellung in mehrere Teilbereiche die sich wahlweise Umschalten lassen
Zusätzliche Module lokal installieren:
npm install react-router-dom (kap14xx)
npm ls
c:...\my-workspace\block10-react\node_modules
...
+-- [email protected] extraneo
+-- [email protected] extraneous
import { Route, Link, BrowserRouter as Router, Switch } from 'react-router-dom'
const routing = (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/users">Users</Link>
</li>
<li>
<Link to="/contact">Contact</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/" component={App} />
<Route path="/users" component={Users} />
<Route path="/contact" component={Contact} />
<Route component={Notfound} />
</Switch>
</div>
</Router>
)
ReactDOM.render(routing, document.getElementById('app'))
3 Componenten (App, Input, Output)
1 Utility-Klasse Kreisrechner
// Create Store -------------------------------------------------------
import { createStore } from 'redux'
import counterReducer from './reducer';
//Native
const store = createStore(counterReducer)
const counterReducer = (state = 0, action) => { // state = 0 init des Store d.h der hält hier ein Objekt vom Type Numer (state)
switch (action.type) {
case "INCREMENT":
return state + 1
case "DECREMENT":
return state - 1
default:
return state
}
}
export default counterReducer
console.log(store)
{liftedStore: {…}, dispatch: ƒ, subscribe: ƒ, getState: ƒ, replaceReducer: ƒ, …}
@@observable: ƒ ()
dispatch: ƒ e(r)
getState: ƒ i()
liftedStore: {dispatch: ƒ, subscribe: ƒ, getState: ƒ, replaceReducer: ƒ, @@observable: ƒ}
replaceReducer: ƒ replaceReducer(r)
subscribe: ƒ subscribe(listener)
[[Prototype]]: Object
console.log("Store-State: " + store.getState());
/ / -> 0
const render = () => ReactDOM.render(<Counter />, document.getElementById('root')) // Erzeugen meiner Haupt-Componente in einer Konstanten
render() // Haupt-Componente wird gerendert
store.subscribe(render); // Haupt-Componente meldet sich als Subscribe am Store an
// Create a Counter component by using the redux state. ---------------
function increment() {
store.dispatch({ type: "INCREMENT" });
console.log("Store-State: " + store.getState());
}
function decrement() {
store.dispatch({ type: "DECREMENT" });
console.log("Store-State: " + store.getState()) ;
}
const Counter = () => {
return (
<div>
<h1>{store.getState()}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
)
}
// Subscribe method ---------------------------------------------------
const builder = () => ReactDOM.render(<Counter />, document.getElementById('root'));
builder();
store.subscribe(builder);
const data = { username: 'example' };
fetch('https://example.com/profile', {
method: 'POST', // or 'PUT'
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
})
.catch((error) => {
console.error('Error:', error);
});
Alles was ich mitschrieb und verstanden habe ist ohne Gewähr. Die Bilder stammen teilweise aus dem Internet und wir haben keine Urheberansprüche darauf.
Besten Dank an unseren sehr empfehlenswerten
Ich habe jetzt erst mal wieder eine Recap-Woche, aber danach geht’s weiter, so: stay tuned!
Gruß, Achim Mertens