The tutorial avoids using nested components doing this:
render() {
const status = 'Next player: X';
return (
<div>
<div className="status">{status}</div>
<div className="board-row">
{this.renderSquare(0)}
{this.renderSquare(1)}
{this.renderSquare(2)}
</div>
<div className="board-row">
{this.renderSquare(3)}
{this.renderSquare(4)}
{this.renderSquare(5)}
</div>
<div className="board-row">
{this.renderSquare(6)}
{this.renderSquare(7)}
{this.renderSquare(8)}
</div>
</div>
);
}
Suppose one wanted to nest the cells inside a row like this:
function Square(props) {
return (
<div onClick={props.onClick} className="square">
[{props.row}.{props.cell}]
</div>
);
}
function Row(props) {
return <div className="row">{props.children}</div>;
}
function Cells(props) {
let squares = [];
for (let i = 0; i < COLUMNS; i++) {
squares.push(<Square row={props.row} cell={i} onClick={props.onClick} />);
}
return squares;
}
Then Inside Board class how one would create a callback with both row and column? I'm able to pass row, but I can't see how to pass the column click as this is actually inside Cells. The state is lifted up in Board.
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
board: [
[{ value: 1, visible: 0 }, { value: 2, visible: 0 }],
[{ value: 2, visible: 0 }, { value: 1, visible: 0 }]
],
player_turn: 0, //0,1,2,3
player1: 0,
player2: 0
};
}
handleClick(e) {
alert(e.row + "." + e.cell);
}
renderSquares() {
const rows = [];
for (let i = 0; i < ROWS; ++i) {
let event = { row: i, cell: -1 };
rows.push(
<Row>
<Cells
row={i}
values={this.state.board[i]}
onClick={() => this.handleClick(event)}
event={event}
/>
</Row>
);
}
return rows;
}
render() {
return <div>{this.renderSquares()}</div>;
}
}
Just pass down column and handleClick! Check out the code below. And I don't really see what the Cells component is doing, so didn't keep that, but you could achieve the same thing even if you kept Cells. Now Row has a bunch of Squares as children which makes sense to me :)
function Square({ row, column, onClick, cell }) {
return (
<div className="square" onClick={() => onClick({ row, column, ...cell })}>
[{row}.{column}]
</div>
);
}
function Row({ children }) {
return <div className="row">{children}</div>;
}
class Board extends React.Component {
constructor(props) {
super(props);
this.state = {
board: [
[{ value: 1, visible: 0 }, { value: 2, visible: 0 }],
[{ value: 2, visible: 0 }, { value: 1, visible: 0 }]
],
playerTurn: 0, //0,1,2,3
player1: 0,
player2: 0
};
}
handleClick({ row, column, value, visible }) {
let { playerTurn } = this.state;
playerTurn++;
this.setState({ playerTurn });
alert(`${row}.${column} - value: ${value}, visible: ${visible} - player turn: ${playerTurn}`);
}
renderSquares() {
return this.state.board.map((cells, row) => (
<Row key={row}>
{cells.map((cell, column) => (
<Square
cell={cell}
key={column}
row={row}
column={column}
onClick={this.handleClick.bind(this)}
/>
))}
</Row>
));
}
render() {
return <div>{this.renderSquares()}</div>;
}
}
ReactDOM.render(<Board />, document.getElementById("app"));
.row {
display: flex;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></app>
Related
In the TableAndInfo Component, each row's parent element is rendered by the renderEls function and the content is returned by the renderSection function. This passes in the content in the sections property as a parameter, as well as the class of the parent container. Passing the props value of date, time, and current in the sections array results in a successful initial render, but they do not continue updating when the state changes. I have inspected the code with the React Developer Tools and I see that the state is being updated by the functions defined in the App function and other components. How do I ensure that the grandchildren elements are re-rendered when the state is updated? Sorry if this doesn't make sense, I was having trouble trying to explain the problem. Thanks in advance!
function App() {
var [component, updateView] = useState('ServerFunctions');
var updateDateAndTime = function(formatDate) {
setInterval(function() {
if (document.getElementsByClassName('date')[0] && document.getElementsByClassName('time')[0]) {
updateDate(formatDate('date'));
updateTime(formatDate('time'));
}
}, 1000);
};
useEffect(() => {
updateDateAndTime(formatDate);
});
var [date, updateDate] = useState(formatDate('date'));
var [time, updateTime] = useState(formatDate('time'));
switch(component) {
case 'Welcome':
return (<Welcome updateView={updateView} date={date} time={time} backspacePinpad={backspacePinpad} />);
case 'ServerFunctions':
return (<ServerFunctions updateView={updateView} date={date} time={time} />);
default:
return null;
}
}
class TablesAndInfo extends React.Component {
sections = [[['info-row pct-space-b', 'flex between full-h'], {
1: ['Menu', 'button', 'gray white-f clickable roundish quarter-w label clickable'],
2: [this.props.current, 'div', 'f-override white-b small left three-quarter-w no-txt-overflow'],
}], [['info-table full-h', 'flex full-h'], {
1: ['Another Round', 'button', 'info-button blue white-f clickable'],
2: ['Select All', 'button', 'info-button gray white-f clickable'],
3: ['Name Check', 'button', 'info-button yellow clickable'],
}], [['tables-section full-h', 'tables-section full-h white-b'], {
}], [['new-table-b full-h', 'new-table-b med single round label full-h full-w'], {
1: ['New Table', 'button', 'blue white-f med single clickable round label clickable full-h full-w']
}]];
renderEls(num, classes) {
return (
<div className={classes}>
{this.sections[num].map((child, key) => {
if (typeof child === 'object' && !(child instanceof Array)) {
return (
<div className={this.sections[num][0][0]}>{this.renderSection(child, this.sections[num][0][1], key)}</div>
)
} else {
return null;
}
})}
</div>
)
}
renderSection(obj, parentClass) {
return (
<div className={parentClass}>
{Object.keys(obj).map((key, i) => {
if (obj[key][1] === 'button') {
return (
<button key={i} className={"flex center " + obj[key][2]}>{obj[key][0]}</button>
)
} else {
return (
<div key={i} className={"flex center " + obj[key][2]}>{obj[key][0]}</div>
)
}
})}
</div>
)
}
render() {
return (
<div className="tables space-r">
<div className="info tables-section">
{this.renderEls(0, 'info-table info-r')}
{this.renderEls(1, 'info-table info-b')}
</div>
{this.renderEls(2, 'table-view tables-section full-h pct-space-b')}
{this.renderEls(3, 'new-table')}
</div>
)
}
class ServerFunctions extends React.Component {
return (
<div className="App ServerFunctions">
<Header signOff={this.signOff} renderBttnRow={this.renderBttnRow} />
<div className="container order-control flex space-b">
<SelectedItems />
<TablesAndInfo current={this.state.current} date={this.props.date} time={this.props.time} />
<Functions current={this.state.current} />
</div>
<Footer current={this.state.current} renderBttnRow={this.renderBttnRow} />
</div>
)
}
}
Ended up solving this! Here's what I added in the TablesAndInfo Component:
constructor(props) {
super(props);
this.state = {current: this.props.current, date: this.props.date, time: this.props.time}
}
shouldComponentUpdate(nextProps) {
if ((nextProps.current !== this.state.current) || (nextProps.date !== this.state.date) || (nextProps.time !== this.state.time)) {
this.setState({current: nextProps.current});
this.setState({date: nextProps.date});
this.setState({time: nextProps.time});
}
return true;
}
componentDidUpdate() {
this.sections[0][1][2][0] = this.state.current;
this.sections[0][2][1][0] = this.state.time;
this.sections[0][2][2][0] = this.state.date;
}
Sorry for my English)
Do not judge strictly, since I just started working with the react.
I made a simple slider on the react and now I want to make it cyclic.
But I can’t. In my code it seems to be cyclic, but for some reason it skips the last picture.
how can i fix it?
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{
id: 1,
name: "Product 1",
price: 50,
q: 0,
category: "Sporting Goods",
images: [
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property04.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property02.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property01.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property03.jpg"
],
currentImageIndex: 0,
isCycleMode: false,
cantGoPrev: false,
cantGoNext: true
},
{
id: 2,
name: "Product 2",
price: 70,
q: 0,
category: "Sporting Goods",
images: [
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property04.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property02.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property01.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property03.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property02.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property01.jpg",
"https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property03.jpg"
],
currentImageIndex: 0,
isCycleMode: false,
cantGoPrev: false,
cantGoNext: true
}
]
};
}
nextSlideHandler = (e, item, index ) => {
let arr = [...this.state.data];
let newIndex = this.state.data[index].currentImageIndex;
if (e.currentTarget.dataset.direction === "next") {
if (newIndex < this.state.data[index].images.length - 1) {
newIndex = this.state.data[index].currentImageIndex + 1;
arr[index].cantGoPrev = true;
this.setState({data:arr})
}
if (newIndex === this.state.data[index].images.length - 1) {
newIndex = 0;
arr[index].cantGoNext = true;
this.setState({data:arr})
}
} else {
if (newIndex > 0) {
newIndex = this.state.data[index].currentImageIndex - 1;
arr[index].cantGoNext = true;
this.setState({data:arr})
}
if (newIndex === 0) {
arr[index].cantGoPrev = false;
this.setState({data:arr})
}
}
arr[index].currentImageIndex = newIndex;
this.setState({ data:arr });
}
render() {
return (
<div className="App">
<div>
<h3>Products</h3>
<div className="collection">
{this.state.data.map((item, index) => (
<div key={item.id} className="product">
<div className="product__image">
<div>
<button
disabled={!item.cantGoPrev}
data-direction="prev"
onClick={(e)=> this.nextSlideHandler(e,item, index)}
className="prev"
>
prev
</button>
</div>
<div>
<img
src={item.images[item.currentImageIndex]}
alt=""
/>
{item.images.currentImageIndex}
</div>
<div>
<button
disabled={!item.cantGoNext}
data-direction="next"
onClick={(e)=> this.nextSlideHandler(e, item, index)}
className="next"
>
next
</button>
</div>
</div>
<div className="product__name">{item.name}</div>
<div className="product__price">{item.price}</div>
</div>
))}
</div>
</div>
</div>
);
}
}
What is the best way to write a slider?
I will be glad of any help
First: There are many ways to achieve what you are trying to do, but this is how I would have done it.
Instead of using index to update different state, I would make a own component for Product. Inside that component you have full access to that products state and props. This makes it easier to work with the correct data.
I would also remove the next/previous-logic from the state and just do a check if the buttons should be active on render.
class App extends React.Component {
constructor (props) {
super(props)
this.state = {
data: [
{
id: 1,
name: 'Product 1',
price: 50,
q: 0,
category: 'Sporting Goods',
images: [
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property04.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property02.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property01.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property03.jpg'
],
currentImageIndex: 0,
isCycleMode: false
},
{
id: 2,
name: 'Product 2',
price: 70,
q: 0,
category: 'Sporting Goods',
images: [
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property04.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property02.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property01.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property03.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property02.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property01.jpg',
'https://ihatetomatoes.net/demos/_rw/01-real-estate/tn_property03.jpg'
],
currentImageIndex: 0,
isCycleMode: false
}
]
}
}
handleChange = (arr) => {
// State updates based on other state should be asynchronous
// https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
this.setState((state, props) => {
const oldArr = [...state.data]
const arrIndex = oldArr.findIndex(x => x.id === arr.id)
oldArr[arrIndex] = arr
return ({
data: oldArr
})
})
}
render () {
return (
<div className='App'>
<div>
<h3>Products</h3>
<div className='collection'>
{this.state.data.map((item) => (
<Product
item={item}
key={item.id}
onChange={this.handleChange}
/>
))}
</div>
</div>
</div>
)
}
}
class Product extends React.Component {
handleSlideChange = (e) => {
const arr = { ...this.props.item }
if (e.currentTarget.dataset.direction === 'next') {
arr.currentImageIndex++
} else {
arr.currentImageIndex--
}
this.props.onChange(arr)
};
render () {
const { item } = this.props
return (
<div key={item.id} className='product'>
<div className='product__image'>
<div>
<button
disabled={item.currentImageIndex <= 0}
data-direction='prev'
onClick={this.handleSlideChange}
className='prev'
>
Prev
</button>
</div>
<div>
<img src={item.images[item.currentImageIndex]} alt='' />
{item.images.currentImageIndex}
</div>
<div>
<button
disabled={item.currentImageIndex >= item.images.length - 1}
data-direction='next'
onClick={this.handleSlideChange}
className='next'
>
Next
</button>
</div>
</div>
<div className='product__name'>{item.name} {item.currentImageIndex}</div>
<div className='product__price'>{item.price}</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I have a class component(actually the collection of the same components) where I have 2 buttons + and - to increase and decrease quantity of watches. Min amount of watches is 1 and max amount is 10. I have regulated this with this 2 functions increaseAmountHandler and decreaseAmountHandler. With this two buttons it's all ok. But the problem is that I have to sum up the value of calculated watches in parent component and I cannot forward the summed up values of the watches to a parent component to state variable totalAmount. Cannot use Redux becacuse it's a collection of watches component and each have own + and - button already occupied with this 2 increaseAmountHandler, decreaseAmountHandler functions.
Anyone idea how to solve this?
Child component:
import React, { Component } from 'react';
import Modal from '.././UI/Modal';
class SelectedWatch extends Component {
constructor(props) {
super(props)
this.state = {
watchQuantity: 1,
watchAmount: 1
}
}
increaseAmountHandler = () => {
if(this.state.watchQuantity < 1) {
this.setState({
watchQuantity: 0,
watchAmount: 0
})
return;
} else if (this.state.watchQuantity >= 10){
this.setState({
watchQuantity: 10,
watchAmount: this.props.selectedWatch.selectedWatchPrice * this.state.watchQuantity
})
return;
}
this.setState({
watchQuantity: this.state.watchQuantity + 1,
watchAmount: this.props.selectedWatch.selectedWatchPrice * this.state.watchQuantity
})
}
decreaseAmountHandler = () => {
if(this.state.watchQuantity < 1) {
this.setState({
watchQuantity: 0,
watchAmount: 0
})
return;
} else if (this.state.watchQuantity >= 10){
this.setState({
watchQuantity: 9,
watchAmount: this.props.selectedWatch.selectedWatchPrice * this.state.watchQuantity
})
return;
}
this.setState({
watchQuantity: this.state.watchQuantity - 1,
watchAmount: this.props.selectedWatch.selectedWatchPrice * this.state.watchQuantity
})
}
render() {
return (
<div className={"shopping-cart-product" + (this.state.watchQuantity < 1 ? ' notDisplayed' : '')}>
<div className="product-info">
<div>
<h3>{this.props.selectedWatch.selectedWatchName}</h3>
<p>${this.props.selectedWatch.selectedWatchPrice} × {this.state.watchQuantity}</p>
</div>
<img src={this.props.selectedWatch.selectedWatchUrl} />
</div>
<div className="product-count">
<button onClick={this.decreaseAmountHandler}>-</button>
<span>{this.state.watchQuantity}</span>
<button onClick={this.increaseAmountHandler}>+</button>
</div>
</div>
);
}
}
export default SelectedWatch;
Parent component:
import React, { Component } from 'react';
import EnteredWatch from '.././components/EnteredWatch/EnteredWatch';
import SelectedWatch from '.././components/SelectedWatch/SelectedWatch';
class App extends Component {
constructor(props) {
super(props)
this.state = {
watchName: '',
watchDescription: '',
watchUrl: '',
watchPrice: '',
watchId: '',
watchAmount: '',
watchQuantity: 1,
enteredWatchList: [],
selectedWatchName: '',
selectedWatchDescription: '',
selectedWatchUrl: '',
selectedWatchPrice: '',
selectedWatchId: '',
selectedWatchAmount: '',
selectedWatchQuantity: 1,
selectedWatchList: [],
totalAmount: 0,
}
}
submitHandler = (event) => {
event.preventDefault();
let watchId = Math.floor((Math.random() * 100) + 1);
let watchName = this.state.watchName;
let watchDescription = this.state.watchDescription;
let watchUrl = this.state.watchUrl;
let watchPrice = this.state.watchPrice;
let watchQuantity = this.state.watchQuantity;
this.setState({
enteredWatchList: this.state.enteredWatchList.concat({watchName, watchUrl, watchDescription, watchPrice, watchId, watchQuantity})
})
add = (selectedWatchName, selectedWatchUrl, selectedWatchDescription, selectedWatchPrice, index, selectedWatchQuantity) => {
let arr = this.state.selectedWatchList;
let found = arr.some(el => {
return el.selectedWatchName === selectedWatchName;
});
if (!found) {
return arr.concat({selectedWatchName, selectedWatchUrl, selectedWatchDescription, selectedWatchPrice, index, selectedWatchQuantity});
} else {
return this.state.selectedWatchList;
}
}
buyWatchHandler = (selectedWatchName, selectedWatchUrl, selectedWatchDescription, selectedWatchPrice, index, selectedWatchQuantity) => {
let arr = this.add(selectedWatchName, selectedWatchUrl, selectedWatchDescription, selectedWatchPrice, index, selectedWatchQuantity);
this.setState({
selectedWatchName: selectedWatchName,
selectedWatchUrl: selectedWatchUrl,
selectedWatchDescription: selectedWatchDescription,
selectedWatchPrice: selectedWatchPrice,
selectedWatchId: index,
selectedWatchQuantity: selectedWatchQuantity,
selectedWatchList: arr
});
}
render() {
const enteredWatches = this.state.enteredWatchList.map((enteredWatch, index) => {
return <EnteredWatch
key={index}
enteredWatch={enteredWatch}
selected={this.buyWatchHandler.bind(this, enteredWatch.watchName, enteredWatch.watchUrl,
enteredWatch.watchDescription, enteredWatch.watchPrice, index, enteredWatch.watchQuantity)}
/>
});
const selectedWatches = this.state.selectedWatchList.map((selectedWatch, index) => {
const active = this.state.activeIndex;
return <SelectedWatch
key={index}
active={index === active}
selectedWatch={selectedWatch}
/>
});
return (
<div className="App">
<div className="container-fluid">
<div className="container">
<div className="add-product">
<form>
<div>
<label>Product name:</label>
<input
type="text"
placeholder="Casio Watch"
required
value={this.state.watchName}
onChange={event => this.setState({watchName: event.target.value})}
/>
</div>
<div>
<label>Product description:</label>
<textarea
placeholder="Sample description..."
value={this.state.watchDescription}
onChange={event => this.setState({watchDescription: event.target.value})}
>
</textarea>
</div>
<div>
<label>Product image:</label>
<input
type="text"
placeholder="http://...jpg"
value={this.state.watchUrl}
pattern="https?://.+" required
onChange={event => this.setState({watchUrl: event.target.value})}
/>
</div>
<div>
<label>Product price:</label>
<input
type="number"
min="0"
placeholder="22"
value={this.state.watchPrice}
onChange={event => this.setState({watchPrice: event.target.value})}
/>
</div>
<button
type="submit"
onClick={event => this.submitHandler(event)}
>
Add a new Task
</button>
</form>
</div>
<div className="list-products">
<ul>
{enteredWatches}
</ul>
</div>
<div className="shopping-cart">
<div className="shopping-cart-products">
<ul>
{selectedWatches}
</ul>
</div>
<div className="shopping-cart-summary">
<div>Total: <b>${this.state.totalAmount}</b></div>
<div><button onClick={this.summaryHandler}>Purchase</button></div>
</div>
</div>
</div>
</div>
</div>
);
}
}
export default App;
The parent has to keep track of how many watches have been added.
Make the parent smart (has state), and the children dumb (no state).
Manage all the state in the parent, and put the click handlers in the parent too.
Pass those handlers down to the child, to be fired when its buttons are clicked. Something like this:
class Parent extends React.Component {
this.state = {
cart: [],
watches: [
{ id: 1, name: "Casio", description: "...", price: 25 },
{ id: 2, name: "Rolex", description: "...", price: 3000 },
{ id: 3, name: "Timex", description: "...", price: 10 },
],
}
handleClickIncrement = (watchId) => {
//add it to the cart (or increment it if its already there)
}
handleClickDecrement = (watchId) => {
//remove it from the cart (or deccrement it if its already there)
}
getCartTotal() {
//loop over cart and calculate
}
renderWatches() {
this.state.watches.map(watch => (
<Watch id={watch.id}
name={watch.name}
description={watch.description}
price={watch.price}
onClickIncrement={() => { this.handleClickIncrement(watch.id); }}
onClickDecrement={() => { this.handleClickDecrement(watch.id); }}
))
}
render() {
<div>
<h1>Our selection of watches:</h1>
{this.renderWatches()}
<h1>Your cart total: {this.getCartTotal()}</h1>
</div>
}
}
class Watch extends React.Component {
props = {
id,
name,
description,
price,
quantityInCart,
onClickIncrementButton,
onClickDecrementButton
}
render() {
<div>
<h1>{this.props.name}</h1>
<p>{this.props.description}</p>
<h5>${this.props.price}</h5>
<button onClick={this.props.onClickIncrementButton}>+</button>
<button onClick={this.props.onClickDecrementButton}>-</button>
</div>
}
}
Let me preface that I'm in the process of learning React and I'm still pretty green at this.
I'm going to give the necessary parts of the code:
I have built a counter with increment and decrement buttons that are utilized by ways of state, they work just fine until I introduce and array and map over it. Then things start to break down. I know my code is wrong, I know there's something amiss however I'm completely ignorant as to what to even look for.
In my counting.js I have:
const players = [
{
name: "Jon Smith",
score: 10,
id: 1,
},
{
name: "Jon Doe",
score: 40,
id: 2,
},
{
name: "John Ham",
score: 30,
id: 3,
},
];
Which I have mapped in here:
class Counting extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0,
nameof: 'Full Name',
}
this.incrementCount = this.incrementCount.bind(this)
this.decrementCount = this.decrementCount.bind(this)
}
incrementCount(e) {
this.setState({
count: (this.state.count + 1),
})
}
decrementCount(e) {
this.setState({
count: (this.state.count - 1),
})
}
render() {
const listPlayers = players.map((players) =>
<Counter
key={players.id}
incrementCount={this.incrementCount}
decrementCount={this.decrementCount}
nameof={players.name}
count={players.score}
/>
);
return (
<div className="wrap">
<Titles header="Counter" subhead="A setState() project" subtitle="this will change" />
<h3>This doesn't work correctly</h3>
<ul>{listPlayers}</ul>
<ScoreList>
<h3> works</h3>
<li>
<Counter
incrementCount={this.incrementCount}
decrementCount={this.decrementCount}
nameof={this.state.nameof}
count={this.state.count}
/>
</li>
<li>
<Counter
incrementCount={this.incrementCount}
decrementCount={this.decrementCount}
nameof={this.state.nameof}
count={this.state.count}
/>
</li>
</ScoreList>
</div>
)
}
}
I have imported my Counter.jswhich is comprised of:
class Counter extends Component {
render() {
const { count } = this.props
const { decrementCount } = this.props
const { incrementCount } = this.props
const { nameof } = this.props
return (
<div>
<CountCell>
<Row style={{alignItems: 'center'}}>
<Col>
<CountButton
onClick={incrementCount}>
<Icon
name="icon" className="fa fa-plus score-icon"
/>
</CountButton>
</Col>
<Col >
<ScoreName>{nameof}</ScoreName>
</Col>
<Col >
<Score>{count}</Score>
</Col>
<Col>
<CountButton
onClick={decrementCount}>
<Icon
name="icon" className="fa fa-minus score-icon"
/>
</CountButton>
</Col>
</Row>
</CountCell>
</div>
)
}
}
So the increment and decrement buttons are only working globally and only for my static <li>, not my ones generated from the array. If I'm making any sense at all, how do I individually map my inc/dec buttons to each <li> and not globally?
Thank you!
You need to keep the state also be an array of objects, each storing data for a corresponding user
class Counting extends React.Component {
constructor(props) {
super(props);
this.state = {
countInfo: []
}
this.incrementCount = this.incrementCount.bind(this)
this.decrementCount = this.decrementCount.bind(this)
}
incrementCount(index) {
var countInfo = [...this.state.countInfo];
if(countInfo[index]) {
countInfo[index].count = countInfo[index].count + 1
countInfo[index].nameOf = players[index].name
}
else {
countInfo[index] = {count: 1, nameOf: players[index].name}
}
this.setState({
countInfo
})
}
decrementCount(index) {
var countInfo = [...this.state.countInfo];
if(countInfo[index]) {
countInfo[index].count = countInfo[index].count - 1
countInfo[index].nameOf = players[index].name
}
else {
countInfo[index] = {count: -1, nameOf: players[index].name}
}
this.setState({
countInfo
})
}
render() {
const listPlayers = players.map((players, index) =>
<Counter
key={players.id}
incrementCount={() => this.incrementCount(index)}
decrementCount={() => this.decrementCount(index)}
nameof={players.name}
count={players.score}
/>
);
return (
<div className="wrap">
<Titles header="Counter" subhead="A setState() project" subtitle="this will change" />
<h3>This doesn't work correctly</h3>
<ul>{listPlayers}</ul>
I have some problems understanding how you interact with element in React after rendering. I currently got this fiddle: https://jsfiddle.net/y7dh3vh5/
var items = ["Afghanistan","Albania","Algeria","Andorra","Angola"....
var RepeatModule = React.createClass({
getDefaultProps: function() {
return { items: [] }
},
render: function() {
var listItems = this.props.items.map(function(item, i) {
return (
<div className="item" key={i}>
<p>{item}</p>
</div>
);
});
return (
<div>
{listItems}
</div>
);
}});ReactDOM.render(<RepeatModule items={items} />, document.getElementById('itemList'));
And I'm looking for a way to highlight a random country when I press the "Highlight random country"-button. Is there an easy way to implement this?
Thanks in advance.
Add state to save highlightedIndex:
getInitialState () {
return {
highlightedIndex: -1
}
},
Add method for button
setNewTarget: function() {
var l = this.props.items.length - 1;
this.setState({
highlightedIndex: this.randomInteger(0, l)
})
},
Put button into return render
return (
<div>
<button onClick={this.setNewTarget}>
Highlight random country
</button>
{listItems}
</div>
);
Live example: https://jsfiddle.net/ufmagg4o/
Just keep your index element in state and compare it in map method. I prefere es6 so i hope you will understand
class Example extends React.Component {
constructor(){
this.state = {
items: ['hello', 'world', 'random', 'highlight'],
index: null
}
this.click = this.click.bind(this);
}
click(){
const items = this.state.items;
const index = Math.floor(Math.random()*items.length);
this.setState({index: index})
}
render(){
const list = this.state.items.map((item,index)=>{
return <p key={index} className={this.state.index === index ? 'item': null}>
{item}
</p>
})
return <div>
<button onClick={this.click}>Click me</button>
{list}
</div>
}
}
React.render(<Example />, document.getElementById('container'));
fiddle example
Thanks!
The key is to keep that button inside React. Don't try to manipulate React components from outside React.
<script src="https://facebook.github.io/react/js/jsfiddle-integration-babel.js"></script>
<div id="itemList">
</div>
.highlighted {
color: white;
background-color: tomato;
}
var items = [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola"
]; // etc...
var RepeatModule = React.createClass({
getDefaultProps: function() {
return { items: [] }
},
getInitialState() {
return {highlighted: null};
},
onClickButton() {
this.setState({
highlighted: (Math.random() * this.props.items.length)|0
});
},
render: function() {
var _this = this;
var listItems = this.props.items.map(function(item, i) {
return (
<div className="item" key={i}>
<p className={i == _this.state.highlighted ? 'highlighted' : null}>
{item}
</p>
</div>
);
});
return (
<div>
<button onClick={this.onClickButton}>
Highlight random country
</button>
<div>
{listItems}
</div>
</div>
);
}
});
ReactDOM.render(<RepeatModule items={items} />, document.getElementById('itemList'));
jsfiddle
ES2015 version:
const items = [
"Afghanistan",
"Albania",
"Algeria",
"Andorra",
"Angola"
]; // etc...
class RepeatModule extends React.Component {
static propTypes = {items: React.PropTypes.arrayOf(React.PropTypes.string)};
static defaultProps = {items: []};
state = { highlighted: null };
onClickButton = () => {
this.setState({
highlighted: (Math.random() * this.props.items.length)|0
});
};
render() {
const listItems = this.props.items.map((item, i) => (
<div className="item" key={i}>
<p className={i == this.state.highlighted ? 'highlighted' : null}>
{item}
</p>
</div>
));
return (
<div>
<button onClick={this.onClickButton}>
Highlight random country
</button>
<div>
{listItems}
</div>
</div>
);
}
});
ReactDOM.render(<RepeatModule items={items} />, document.getElementById('itemList'));