Render classes dynamically
Render lists
Conditional rendering
Handling events
Composing components
Passing data to components
State vs props
Data can be passed through attributes
<PokeCounter key={counter.id} count={counter.count} anotherAttribute="a value I pass to my component"/>
You can then retrieve the data from a plain JavaScript object called props
key will not be passed as it is a special keyword used to identify elements in a list
pokeCounter.jsx
import React, { Component } from "react";
import PokeCounter from "./pokeCounter";
class PokeCounters extends Component {
state = {
counters: [
{ id: 1, value: 0 }
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
{ id: 5, value: 0 }
]
};
render() {
return (
<div>
{this.state.counters.map(counter =>
<PokeCounter
key={counter.id}
value={counter.value}
/>
)}
</div>
);
}
}
export default PokeCounters;
import React, { Component } from "react";
class PokeCounter extends Component {
state = {
value: this.props.value
};
/** [...] */
incrementCount = () => {
this.setState({
value: this.state.value + 1
});
};
render() {
return (
<React.Fragment>
<span
className={this.getBadgesClasses()}>
{this.formatCount()}
</span>
<button
onClick={this.incrementCount}
className="btn btn-secondary btn-sm"
>
Increment
</button>
</React.Fragment>
);
}
}
export default PokeCounter;
pokeCounters.jsx
In case of a complex element, you can pass data as children props
<PokeCounter key={counter.id} count={counter.count}>
<h1>Hello world</h1>
</PokeCounter>
data is then accessible in props.children
Available in Chrome and Firefox
2
Increment
3
5
0
0
Delete
Increment
Delete
Increment
Delete
Increment
Delete
Increment
Delete
Let's add "Delete" button
PokeCounters
PokeCounter
state = {
counters: []
};
Delete button
How to update that state?
The component that owns a piece of state should be the one modifying it
PokeCounters
PokeCounter
handleDelete
onDelete
class PokeCounters extends Component {
/** [...] */
handleDelete = () => {
console.log("Event handler called");
};
render() {
return (
<div>
{this.state.counters.map(counter => (
<PokeCounter
key={counter.id}
value={counter.value}
onDelete={this.handleDelete}
/>
))}
</div>
);
}
}
class PokeCounter extends Component {
/** [...] */
render() {
return (
<div>
/** [...] */
<button
className="btn btn-danger btn-sm-m-2"
onClick={this.props.onDelete}
>
Delete
</button>
</div>
);
}
}
class PokeCounters extends Component {
/** [...] */
handleDelete = counterId => {
const counters = this.state.counters.filter(c => c.id !== counterId);
this.setState({ counters });
};
render() {
return (
<div>
{this.state.counters.map(counter => (
<PokeCounter
key={counter.id}
value={counter.value}
id={counter.id}
onDelete={this.handleDelete}
/>
))}
</div>
);
}
}
class PokeCounter extends Component {
/** [...] */
render() {
return (
<div>
/** [...] */
<button
className="btn btn-danger btn-sm-m-2"
onClick={() => this.props.onDelete(this.props.id)}
>
Delete
</button>
</div>
);
}
}
class PokeCounters extends Component {
/** ... */
render() {
return (
<div>
{this.state.counters.map(counter => (
<PokeCounter
key={counter.id}
counter={counter}
onDelete={this.handleDelete}
/>
))}
</div>
);
}
}
class PokeCounter extends Component {
state = {
value: this.props.counter.value
};
/** ... */
render() {
return (
<div>
/** ... */
<button
className="btn btn-danger btn-sm-m-2"
onClick={() => this.props.onDelete(this.props.counter.id)}
>
Delete
</button>
</div>
);
}
}
2
Increment
3
5
0
0
Delete
Increment
Delete
Increment
Delete
Increment
Delete
Increment
Delete
Reset
Let's add "Reset" button
class PokeCounters extends Component {
/** [...] */
handleReset = () => {
const counters = this.state.counters.map(c => {
c.value = 0;
return c;
});
this.setState({ counters });
};
render() {
return (
<div>
<button
className="btn btn-primary btn-sm-m-2"
onClick={this.handleReset}
>
Reset
</button>
/** [...] */
</div>
</div>
);
}
}
It does not work because PokeCounter has a local state
PokeCounters
PokeCounter
Controlled component
data (props)
raise events
import React, { Component } from "react";
class PokeCounter extends Component {
formatCount() {
return this.props.counter.value === 0 ? "Zero" : this.props.counter.value;
}
getBadgesClasses() {
let classes = "badge m-2 badge-";
classes += this.props.counter.value === 0 ? "warning" : "primary";
return classes;
}
render() {
return (
<div>
<span className={this.getBadgesClasses()}>{this.formatCount()}</span>
<button
onClick={() => this.props.onIncrement(this.props.counter)}
className="btn btn-secondary btn-sm m-2"
>
Increment
</button>
<button
className="btn btn-danger btn-sm-m-2"
onClick={() => this.props.onDelete(this.props.counter.id)}
>
Delete
</button>
</div>
);
}
}
export default PokeCounter;
import React, { Component } from "react";
import PokeCounter from "./pokeCounter";
class PokeCounters extends Component {
state = {
counters: [
{ id: 1, value: 1 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
{ id: 5, value: 0 }
]
};
handleDelete = counterId => {
const counters = this.state.counters.filter(c => c.id !== counterId);
this.setState({ counters });
};
handleIncrement = counter => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value++;
this.setState({ counters });
};
handleReset = () => {
const counters = this.state.counters.map(c => {
c.value = 0;
return c;
});
this.setState({ counters });
};
render() {
return (
<div>
<button
className="btn btn-primary btn-sm-m-2"
onClick={this.handleReset}
>
Reset
</button>
<div>
{this.state.counters.map(counter => (
<PokeCounter
key={counter.id}
counter={counter}
onDelete={this.handleDelete}
onIncrement={this.handleIncrement}
/>
))}
</div>
</div>
);
}
}
export default PokeCounters;
App
NavBar
PokeCounters
PokeCounter
counters[]
2
+
-
3
+
-
5
+
-
0
+
-
0
+
-
Total
10
Reset
Delete
Delete
Delete
Delete
Delete