Falls noch nicht gemacht:
git clone -b gh-pages https://github.com/DJCordhose/react-workshop.git
npm install
npm run setup
code/workspace
wechselnnpm run devserver
Folien: Im geklonten Verzeichnis 2016_enterjs.html
class HelloMessage extends React.Component {
render() {
return <h1 className='title'>Hello, World!</h1>
}
}
class HelloMessage extends React.Component {
render() {
return (<div>
<input ref={input => this.input = input}
onChange={event => this.updateModel(event)}
value={this.state.greeting} />
<p>{this.state.greeting}, World</p>
<button
onClick={() => this.reset()}>
Clear
</button>
</div>);
}
constructor(props) {
super(props);
this.state = {greeting: this.props.greeting};
}
updateModel(event) {
this.setState({greeting: event.target.value});
}
reset() {
this.setState({greeting: ""});
this.input.focus();
}
}
// index.html
<html>
<body>
</body>
<script src="dist/main.js"></script>
</html>
// main.js
import HelloMessage from './HelloMessage';
const mountNode = document.getElementById('mount');
ReactDOM.render(<HelloMessage greeting="Hello"/>, mountNode);
Run
Nutze das workspace
-Verzeichnis in diesem Repository
Hier ist eine Tool-Chain mit Webpack und Babel vorbereitet
npm install
(auf oberster Ebene)
node setup
(auf oberster Ebene)
cd code/workspace
npm run devserver
const person = 'Oma';
const language = 'Haskell';
console.log(`${person} programs
${10 * 1024 * 1024} lines of code
in ${language}.`);
// Output:
// Oma programs
// 10485760 lines of code
// in Haskell.
{
// no hoisting
console.log(a);
// => ReferenceError: a is not defined
let a = 10;
// or
const a = 10;
a = 20;
// => TypeError: Assignment to constant variable.
}
console.log(a);
// => ReferenceError: a is not defined
class Person {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
}
class Programmer extends Person {
constructor(name, language) {
super(name);
this.language = language;
}
code() {
return this.name + " codes in " + this.language;
}
}
const programmer = new Programmer('Erna', 'JavaScript');
console.log(programmer.code());
console.log(programmer instanceof Programmer); // true
console.log(programmer instanceof Person); // true
const displayInPage = (text) => {
return document.body.innerHTML +=
`${text}
`;
};
const displayInPage = text => document.body.innerHTML += `${text}
`;
const obj = {
methodOfObj: function () {
console.log(`In Method: ${obj === this}`); // true
['1', '2', '3'].forEach( e => {
console.log(`In Loop: ${obj === this}`); // true
});
}
};
obj.methodOfObj();
render
-Methode:{}
)
class GreetingDetail extends React.Component {
render() {
return (
<input ref={input => this.input = input}
onChange={event => this.updateModel(event.target.value)}
value={this.state.greeting} />
{this.state.greeting}, World
);
}
// ...
}
class GreetingDetail extends React.Component {
render() {
return (
<input ref={input => this.input = input}
onChange={event => this.updateModel(event.target.value)}
value={this.state.greeting} />
{this.state.greeting}, World
);
}
updateModel(greeting) {
this.setState({greeting});
}
// ...
}
const name = 'Oma';
const person = {
// ES5: name: name
name,
// ES5: toString: function()
toString() {
return this.name;
}
};
console.log(person.name); // Oma
console.log(person.toString()); // Oma
// Person.js
class Person {
// ...
}
export default Person;
// Programmer.js
import Person from './Person';
export default class Programmer extends Person {
// ...
}
// util.js
export function displayInPage(text) {
document.body.innerHTML +=
`${text}
` ;
}
// or
export { displayInPage };
import {displayInPage} from "./util";
displayInPage('Hello, World');
const person = {
name: 'Olli',
email: 'oliver.zeigermann@gmail.com'
};
const {name, notThere} = person;
console.log(`name=${name}`);
// name=Olli
console.log(`notThere=${notThere}`);
// notThere=undefined
this.props
class TitleComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return <h1>{this.props.title}</h1>
}
// ...
}
<TitleComponent title='Hello World' />
this.state={}
this.state
this.setState()
class GreetingDetail extends React.Component {
constructor(props) {
super(props);
this.state = { name: 'Klaus' };
}
updateModel(event) {
// Zustand ändern: Komponente wird neu gerendert
this.setState({name: event.target.value});
}
render() {
return <input value={this.state.name}
onChange={e => this.updateModel(e)} />
}
// ...
}
class HelloMessage extends React.Component {
render() {
return (
<input ref={input => this.input = input} />
<button
onClick={() => this.input.focus()}>
Focus
</button>
);
}
}
GreetingDetail
um
name
und greeting
im Zustand der Komponente setzen
code/material/1-detail
in deinen public
-Ordner
render
-Methode
function Layout(props) {
return (
{props.children}
);
}
Array.map
const greetings = [{
id: 0,
name: 'Olli',
greeting: 'Huhu'
},
{
id: 1,
name: 'Oma',
greeting: 'Hallo'
}
];
const body = greetings.map(greeting =>
<tr key={greeting.id}>
<td>{greeting.name}
<td>{greeting.greeting}
</tr>);
class GreetingController extends React.Component {
render() {
const {greetings} = this.state;
return (
<GreetingMaster greetings={greetings}
onAdd={() => this.setState({mode: MODE_DETAIL})}
);
}
// ...
}
class GreetingController extends React.Component {
const greeting = { name: 'Klaus', greeting: 'Hello' };
render() {
return <GreetingDetail {...greeting} />
// entspricht:
// <GreetingDetail name='Klaus' greeting='Hello' />
}
}
GreetingDetail.propTypes = {
greeting: PropTypes.shape({
name: React.PropTypes.string.isRequired,
greeting: React.PropTypes.string.isRequired
}),
onAdd: PropTypes.func.isRequired
};
code/material/2-hierarchy
in deinen src-Ordner
GreetingController
die render-Methode, so dass dein Detail-View ebenfalls angezeigt wird, wenn der Benutzer den Add-Button klickt
addGreeting
nutzt
GreetingDetail
brauchst du einen neuen Knopf, der mit dem neuen Gruß den Callback aufruft
react-addons-test-utils
)type
(z.B. div
)Komponente
natives HTML Element
Children
import ReactTestUtils from 'react-addons-test-utils';
const renderer = ReactTestUtils.createRenderer();
renderer.render(
<GreetingMaster greetings={sampleGreetings} />
);
const tree = renderer.getRenderOutput();
// tree enthält virtuellen DOM
. . .
const tree = renderer.getRenderOutput();
expect(tree.type).toBe('div');
expect(tree.props.children.length).toEqual(2); // table ~ button
const table = tree.props.children[0];
expect(table.type).toBe('table');
const tbody = table.props.children[1];
expect(tbody.type).toBe('tbody');
const rows = tbody.props.children;
expect(rows).toEqual([
<tr key='1'><td>Olli</td><td>Huhu</td></tr>,
<tr key='2'><td>Oma</td><td>Hallo</td></tr>
]);
// Ausgangssituation
const nameInput = tree.props.children;
expect(nameInput.props.value).toEqual('Lemmy');
const changeEvent = { target: { value: 'Klaus-Dieter' } };
nameInput.props.onChange(changeEvent);
const updatedTree = renderer.getRenderOutput();
const updatedNameInput = updatedTree.props.children;
expect(updatedNameInput.props.value).toEqual('Klaus-Dieter');
const component = ReactTestUtils.renderIntoDocument(
<GreetingDetail greeting={greeting} />
);
const [nameInput, greetingInput] =
ReactTestUtils.scryRenderedDOMComponentsWithTag(component, 'input');
const [clearButton, saveButton] =
ReactTestUtils.scryRenderedDOMComponentsWithTag(component, 'button');
ReactTestUtils.Simulate.click(clearButton);
expect(nameInput.value).toEqual('');
expect(clearButton.value).toEqual('');
code/material/3-test
in deinen src
-OrdnerGreetingDetail
-Komponente zu rendern
Save
Button
onAdd
-Callback Funktion aufgerufen wurdenpm run test:watch
ausgeführt werden (reagiert auf Code-Änderungen)https://github.com/reactjs/react-router/blob/master/docs/API.md
Route
und IndexRoute
children
-Property
location
und param
Property und componentWillReceiveProps
import { Router, Route, Redirect, IndexRoute, hashHistory }
from 'react-router';
const routes = <Router history={hashHistory}>
<Redirect from="/vote" to="/votes"/>
<Route path="/" component={Layout}>
<IndexRoute component={VotePage}/>
<Route path="votes/:id" component={SingleVotePage}/>
<Route path="login(/:redirect)" component={LoginPage}/>
<Route path="compose" component={VoteComposerPage} onEnter={requireAuth}/>
<Route path="*" component={NoMatchPage}/>
</Route>
</Router>;
const routes = <Router history={hashHistory}>
...
</Router>;
hashHistory
codiert Pfad in angehängten Hash (#/vote)
browserHistory
codiert Pfad direkt in URL (/vote)
// Push a new entry onto the history stack.
history.push('/home')
// Replace the current entry on the history stack.
history.replace('/profile')
// plain match (containing nested routes)
<Route path="/" component={Layout}>
// id is passed as parameter to component
<Route path="votes/:id" component={SingleVotePage}/> /
// redirect is optional
<Route path="login(/:redirect)" component={LoginPage}/>
// onEnter and onExit possible as hooks
<Route path="compose" component={VoteComposerPage}
onEnter={requireAuth}/>
// wildcard
<Route path="*" component={NoMatchPage}/>
</Route>
// /vote redirects to /votes
<Redirect from="/vote" to="/votes"/>
<Route path="/" component={Layout}>
// will be rendered additionally when path is just /
<IndexRoute component={VotePage}/>
// ...
</Route>
children
in den props
übergeben
children
kann grundsätzlich eine oder mehrere Komponenten sein
export default function Layout(props) {
return
Greetings
{props.children}
;
}
a
-Element gerendert
<Link to={`/votes/${vote.id}`} activeClassName='active'>
{vote.title}
</Link>
Der Router übergibt den zu rendernden Routen Daten als Properties
location
: enthält pathname
und query
params
: die dynamischen Segmente der Route (z.B. id
)componentWillReceiveProps
: wird mit neuen Properties aufgerufen, wenn sich die Route geändert hat
code/material/4-router
in deinen src-Ordner
GreetingMaster
enthält Links auf darzustellende Grüße
main.js
eine Route auf GreetingDisplay
hinzu
GreetingDisplay
so dass sie einen solchen Gruß darstellt mit den vom Router übergebenen Parametern ausgibt
https://gruss.firebaseio.com/
: Admin-Interface der Firebase DB
curl 'https://gruss.firebaseio.com/rest/greetings.json'
greetings holen
curl -X PUT -d '[{ "id": 1, "name": "Oma", "greeting": "Hiho"}, {"id": 2, "name": "Opa"}]' 'https://gruss.firebaseio.com/rest/greetings.json'
greetings speichern
curl 'https://gruss.firebaseio.com/rest/greetings.json?orderBy="id"&equalTo=3'
greeting über id laden
fetch(url, {
method: 'PUT',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(json => /* ... */)
.catch(ex => console.error('request failed', ex));
const url = `${BACKEND_URL}${path}`;
return fetch(url)
.then(response => response.json())
.then(json => /* ... */)
.catch(ex => console.error('request failed', ex));
const promise = new Promise(resolve =>
setTimeout(
() => resolve('Result from promise'),
1000)
);
promise.then(value => console.log(value));
// Output after 1 second: Result from promise
const promise = new Promise(resolve =>
setTimeout(
() => resolve('Result from promise'),
1000)
);
// then returns a new promise
const promise2 = promise.then(value => `${value} plus stuff`);
promise2.then(value => console.log(value));
// Output after 1 second: Result from promise plus stuff
Promise
// creates and directly resolves promise
.resolve('Result from promise')
.then(x => {
// this will be printed
console.log(x);
})
.then(() => {
console.log('This will be printed');
})
// this will NOT be printed as no error occured
.catch(e => console.log('error: ', e))
// Output:
// Result from promise
// This will be printed
Promise
// creates and directly resolves promise
.resolve('Result from promise')
.then(x => {
// this will be printed
console.log(x);
throw new Error('Something went wrong');
})
.then(() => {
console.log('This will NOT be printed');
})
// this will be printed
.catch(e => console.log('error: ', e))
// Output:
// Result from promise
// error: [Error: Something went wrong]
Promise
// creates and directly rejects promise
.reject('Promise rejected')
.then(x => {
// this will NOT be printed
console.log(x);
})
.then(() => {
console.log('This will NOT be printed');
})
// this will be printed
.catch(e => console.log('error: ', e))
// Output:
// error: Promise rejected