Could not add multiple counters in react - javascript

I want to make a counter app with increment, decrement and add counter button. But add counter function is not working. It's showing this error:
Parsing error: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...?
i have already tried enclosing it in tags,still its not working.
import React,{Component} from 'react';
import './App.css';
export class App extends Component {
state={
count:0,
}
increment=()=>{
this.setState({ count: this.state.count + 1 });
}
decrement=()=>{
this.setState({ count: this.state.count - 1 });
}
addCounter=()=>{
<span><button onClick={this.increment}>+</button></span>
<span>{this.state.count}</span>
<span><button onClick={this.decrement}>-</button></span>
}
render() {
return (
<div className="App">
<button onClick={this.addCounter}>Add Counter</button>
</div>
)
}
}
export default App;
add counter function should add another counter just below the previous counter.

Basically extract a Counter component.
Then have your App maintain a list of Counter components.
// Counter Component
export class Counter extends React.Component {
state = {
count:0,
}
increment=()=>{
this.setState({ count: this.state.count + 1 });
}
decrement=()=>{
this.setState({ count: this.state.count - 1 });
}
render() {
return (
<div>
<span><button onClick={this.increment}>+</button></span>
<span>{this.state.count}</span>
<span><button onClick={this.decrement}>-</button></span>
</div>
);
}
}
// App Component
export class App extends React.Component {
state = {
counters: [], // additional state for Counter components
}
addCounter = () => {
this.setState({
counters: [
...this.state.counters,
Counter
]
})
}
render() {
return (
<div className="App">
<button onClick={this.addCounter}>Add Counter</button>
{ this.state.counters.map((Counter, index) => (
<Counter key={index} />)
)}
</div>
)
}
}
Demo

You are just adding more spans and button but refering to the same counter.
state={
i : 0,
count:0,
}
var newCount="counter"+this.state.i;
this.setState({
i : this.state.i+1,
})
this.setState({
count: {
...this.state.count,
newCount: 0
}
});
So with this you add a new counter with a progresive autoincrement number.

I think it is what you want to do.
const { useState } = React;
function App(){
const [counter, setCounter] = useState([]);
function addCounter(){
setCounter(counter.concat({id: counter.length, count: 0}));
}
function increase(id){
setCounter(counter.map(el=>{
if(el.id === id){
el.count++;
}
return el;
}));
}
function decrease(id){
setCounter(counter.map(el=>{
if(el.id === id){
el.count--;
}
return el;
}));
}
return (
<div className="App">
<button onClick={addCounter}>Add Counter</button>
{
counter.map(el=>{
return(
<div key={el.id}>
<span>Counter #{el.id}</span>
<div>
<button onClick={()=>{increase(el.id)}}>+</button>
<span>{el.count}</span>
<button onClick={()=>{decrease(el.id)}}>-</button>
</div>
</div>
)
})
}
</div>
)
}
ReactDOM.render(
<App />, document.getElementById('root')
)
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

Your "counter" needs to be a react component with its own state, what you have there will have each "counter" you add use the same component state from App. You also do not save the returned JSX to then render anywhere.
const Counter = () => {
const [count, setCount] = useState(0);
return (
<div> // <-- React components can return only a single node*
<span>
<button onClick={() => setCount(count + 1)}>+</button>
</span>
<span>{count}</span>
<span>
<button onClick={() => setCount(count - 1)}>-</button>
</span>
</div>
);
};
class App extends Component {
state = {
counters: []
};
addCounter = () => {
this.setState(prevState => ({
counters: [...prevState.counters, <Counter />]
}));
};
render() {
return (
<div>
<button onClick={this.addCounter}>Add Counter</button>
{this.state.counters}
</div>
);
}
}
* React render lifecycle function can render arrays.

You have to add another functional Component instead of adding JSX. DEMO
import React from 'react';
const counter = (props) => {
return (
<div>
<span><button onClick={() => props.increment(props.index)}>+</button></span>
<span>{props.count}</span>
<span><button onClick={() => props.decrement(props.index)}>-</button></span>
</div>
)
}
export default counter;
And your main App component
import React, {Component} from 'react';
import Counter from './Counter';
import './App.css';
export class App extends Component {
state={
counters: []
}
valueChanger = (index, inc) => {
this.setState((prevState) => {
const counters = prevState.counters.slice();
counters[index] += inc;
return {
counters: counters
}
});
}
increment=(index)=>{
this.valueChanger(index, 1);
}
decrement=(index)=>{
this.valueChanger(index, -1);
}
addCounter=()=>{
this.setState((prevState) => {
return { counters: [...prevState.counters, 0] }
});
}
render() {
let counterElems = this.state.counters.map((c, index) => {
return <Counter key={index} index={index} increment={this.increment} decrement={this.decrement} count={c} />
});
return (
<div className="App">
{counterElems}
<button onClick={this.addCounter}>Add Counter</button>
</div>
)
}
}
export default App;

Related

How can I write callback func for setState on event click

after onclick event occurs in backpackList.js, fetch data in context.js and then through setState I want to update noneUserCart . After that i want to get data from context.js to backpackList.js to show web page. but the data is inital data []. How can I solve this problem?!
I think this is a Asynchronous problem, but I'm new react, so I don't know how to write code for this. or do I use async, await.
Help me please!
import React, { Component } from 'react';
const ProductContext = React.createContext();
const ProductConsumer = ProductContext.Consumer;
class ProductProvider extends Component {
constructor() {
super();
this.state = {
totalProducts: 0,
isLogin: false,
cartList: [],
isNavOpen: false,
isCartOpen: false,
noneUserCart: [],
};
}
noneUserAddCart = bagId => {
fetch('/data/getdata.json', {
method: 'GET',
})
.then(res => res.json())
.catch(err => console.log(err))
.then(data => {
this.setState(
{
noneUserCart: [...this.state.noneUserCart, data],
},
() => console.log(this.state.noneUserCart)
);
});
};
render() {
return (
<ProductContext.Provider
value={{
...this.state,
handleCart: this.handleCart,
getToken: this.getToken,
addNoneUserCart: this.addNoneUserCart,
hanldeCheckout: this.hanldeCheckout,
openNav: this.openNav,
showCart: this.showCart,
habdleCartLsit: this.habdleCartLsit,
deleteCart: this.deleteCart,
noneUserAddCart: this.noneUserAddCart,
}}
>
{this.props.children}
</ProductContext.Provider>
);
}
}
export { ProductProvider, ProductConsumer };
import React, { Component } from 'react';
import { ProductConsumer } from '../../context';
export default class BackpackList extends Component {
render() {
const {
backpackdata,
backdescdata,
isdescOpen,
showDesc,
descClose,
rangenumone,
rangenumtwo,
} = this.props;
return (
<div>
{backdescdata.map((bag, inx) => {
return (
<>
{isdescOpen && bag.id > rangenumone && bag.id < rangenumtwo && (
<div className="listDescContainer" key={inx}>
<div className="listDescBox">
<ProductConsumer>
{value => (
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
</div>
)}
</ProductConsumer>
<span className="descClosebtn" onClick={descClose}>
X
</span>
</div>
</div>
</div>
)}
</>
);
})}
</div>
);
}
}
fetch is asynchronous, this.setState is yet called when console.log
<div
className="cartBtn"
onClick={() => {
const token = value.getToken();
if (token) {
value.handleCart(bag.id, token);
} else {
value.noneUserAddCart(bag.id);
console.log(value.noneUserCart);
// this part. value.noneUserCart is undefined
}
}}
>
add to cart.
{value.noneUserCart}
{/* when finished, result should show here */}
</div>

How can I use onClick on stateless component in React?

I want to use onClick on a stateless compoenent but it's reject an error like : onClick listener to be a function, instead got a value of object type.
I need to show and hide component on click.
Example when I click on the <ResultCard/> component I want to hide him and show <ResultDetail/>
State React Component :
import React, { Component } from "react";
import ResultCard from "./ResultCard";
import "../../assets/css/Result.css";
import Spinner from "../Spinner";
import { getApiToken, getParisByPrice } from "../../services/api";
import Modal from "../Modal";
import "../../assets/css/BudgetEntry.css";
import modify from "../../assets/images/modify.png";
import ResultDetail from "./ResultDetail";
class Results extends Component {
state = {
priceValue: "",
showResult: true
};
showResultDetail = () => {
this.setState({ showResult: false });
};
closeResultDetails = () => {
this.setState({ showResult: true });
};
render() {
return (
<div className="results-container">
{this.state.loading ? (
<Spinner />
) : (
<div className={"row"}>
{this.state.showResult ?
(
this.state.paris.map(details => (
<ResultCard
key={details.id}
id={details.id}
showResultDetail={this.showResultDetail}
prefix={details.prefix}
costPerDay={details.average_cost_per_day}
logoSports={details.infrastructure.map(home =>
home.logo_path.map(path_image => (
<img
src={path_image}
alt="icon-sports"
style={{ width: 20 }}
key={path_image}
/>
))
)}
/>
))
)
:
(
<ResultDetail closeResultDetail={this.closeResultDetails}/>
)
}
</div>
)}
</div>
);
}
}
export default Results;
ResultCard (who is stateless component):
import React from 'react';
import '../../assets/css/ResultCard.css';
const ResultCard = ({prefix, costPerDay, logoSports, showResultDetail, id}) => {
return (
<div className="card" onClick={showResultDetail} id={id}>
<p style={{margin:5}}>{prefix}</p>
<p style={{margin:1}}>arrondissement</p>
<p>{costPerDay} $</p>
{logoSports}
</div>
)
};
export default ResultCard;
ResultDetail (who is stateless component):
import React from 'react';
const ResultDetail = (closeResultDetail) => (
<div onClick={closeResultDetail}>
<p>Result detail</p>
</div>
)
export default ResultDetail;
thank for your help
The issue is here
const ResultDetail = (closeResultDetail) => (
You need to destructure it from the props object like this:
const ResultDetail = ({closeResultDetail}) => (
Or use it from props directly like this:
const ResultDetail = (props) => (
<div onClick={props.closeResultDetail}>
...
in your state component
showResultDetail = () => {
this.setState({ showResult: false });
};
render() {
....
<ResultCard
....
show={this.state.showResult}
//defer the execution of the method
onClick={(e) => this.showResultDetail(e)}/>
}
resultCard.js
const ResultCard = ({prefix, costPerDay, logoSports, showResultDetail, id, show}) => {
if(show)
return (
<div className="card" onClick={showResultDetail} id={id}>
<p style={{margin:5}}>{prefix}</p>
<p style={{margin:1}}>arrondissement</p>
<p>{costPerDay} $</p>
{logoSports}
</div>
);
};
in Results you define
showResultDetail = () => {
this.setState( {showResult: false });
};
as a function without arguments. You then pass
showResultDetail={(e) => this.showResultDetail(e)}
as a function with an event argument to your ResultCard. If you change that to
showResultDetail={this.showResultDetail}
your problem might already be fixed.
Edit: Here is a minimal snippet that does what you're looking for, I think.
const ResultCard = ({showResultDetail, show}) => {
return <div className="card" onClick={showResultDetail}>{show?'Click me!':''}</div>
};
class Results extends React.Component {
state = {
priceValue: "",
showResult: true
};
showResultDetail = () => {
this.setState({ showResult: false });
};
render() {
return <ResultCard show={this.state.showResult}
showResultDetail={this.showResultDetail}/>
}
}
ReactDOM.render(<Results/>, document.getElementById('root'))
.card {
width: 200px;
height: 50px;
background: lightgray;
text-align: center;
line-height: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id='root'></div>

Why react does not trigger component update?

I have custom list with custom methods. Trying to change list and call setState does not trigger component update
import React, {useState} from "react";
class MyCustomArray extends Array {
push(item) {
super.push(item);
return this;
}
}
export default () => {
const [items, setItems] = useState(new MyCustomArray());
console.log(items)
return (
<div>
{items.length}
<button onClick={() => setItems(items.push(1))}/>
</div>
);
}
I have tried same method using class component instead of function and it works.
import React, {Component} from "react";
class MyCustomArray extends Array {
push(item) {
super.push(item);
return this;
}
}
export default class App extends Component {
constructor(props) {
super(props);
this.state = {items: new MyCustomArray()};
}
render() {
const {items} = this.state;
return (
<div>
{items.length}
<button onClick={() => this.setState({items: items.push(1)})}/>
</div>
);
}
}
You need a new instance of items array, use the spread syntax,
import React, { useState } from "react";
class MyCustomArray extends Array {
push(item) {
super.push(item);
return this;
}
}
export default () => {
const [items, setItems] = useState(new MyCustomArray());
return (
<div>
{items.length}
<button onClick={() => setItems([...items, 1])} />
// ^^^^^^^^^^^^^
</div>
);
};
Same thing if you want to use a class Component :
export default class App extends Component {
constructor(props) {
super(props);
this.state = { items: new MyCustomArray() };
}
updateItems = (item) => {
this.setState(prevState => ({
...prevState,
items: [...prevState.items, item]
}));
}
render() {
const { items } = this.state;
return (
<div>
{items.length}
<button onClick={() => this.updateItems(1)} />
</div>
);
}
}

Div not showing but numbers in React

Following is the UI, in which each box I am trying to display after 1 sec delay - (Box1, 1 sec delay, Box2, 1 sec delay, Box3 ..so on)
Instead I am getting -
My React code and let me know what I am doing wrong here & why its showing numbers -
const CreateBox = (props) => {
return (
<>
{/*<div className="box">{props.num}</div>*/}
<div className="box"></div>
</>
)
}
const App = () => {
return (
<div className="app">
<h3>App</h3>
{
[1,2,3,4,5,6,7,8,9,10].map((item) => {
return setTimeout(() => {
// return (<CreateBox num={item} />)
return (<CreateBox />)
}, 1000)
})
}
</div>
)
}
const root = document.querySelector('#root')
ReactDOM.render(<App />, root)
Codepen - https://codepen.io/anon/pen/pBLPMY
Instead of creating a new timeout for every element in the array on every render, you could create an interval in componentDidMount and increment a number in your state until it reaches 10 and use this number in your render method instead.
Example
class App extends React.Component {
state = {
count: 0
};
componentDidMount() {
const interval = setInterval(() => {
this.setState(
({ count }) => ({ count: count + 1 }),
() => {
if (this.state.count === 10) {
clearInterval(interval);
}
}
);
}, 1000);
}
render() {
return (
<div className="app">
<h3>App</h3>
{Array.from({ length: this.state.count }, (_, index) => (
<CreateBox key={index} num={index + 1} />
))}
</div>
);
}
}
const CreateBox = props => {
return <div className="box">{props.num}</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>
Don't use setTimeout while looping. Instead set the timer inside the CreateBox component using state. If you remove the timeout you can see the boxes. To handle the delay pass the index * 1000 as a timer for each element.
class CreateBox extends React.Component {
state = {
opacity: 0
}
constructor(props){
super(props)
}
componentDidMount(){
setTimeout(()=> this.setState({opacity: 1}),`${this.props.time}000`)
}
render() {
console.log(this.props)
return (
<div style={this.state} className="box">{this.props.num}</div>
)
}
};
const App = () => {
return (
<div className="app">
<h3>App</h3>
{
[1,2,3,'w',5,6,7,8,9,10].map((item, index) => <CreateBox num={item} time={index}/>)
}
</div>
)
}
const root = document.querySelector('#root')
ReactDOM.render(<App />, root)
const CreateBox = (props) => {
return (
<div className="box">{props.num}</div>
)
}
const App = () => {
return (
<div className="app">
<h3>App</h3>
{
[1,2,3,4,5,6,7,8,9,10].map((item) => {
return (<CreateBox num={item} />)
})
}
</div>
)
}
const root = document.querySelector('#root')
ReactDOM.render(<App />, root)

How to render the elements before to filter elements with ReactJS?

I'm doing a project which does a get of the json-server, and render them on the screen.
But when I added a filtering function on it, it only renders after I type a name to filter. I wanted him to render everyone and make the filter.
My Body.js (Where is my function of render):
import React from 'react';
import './Body.css';
import { Link } from "react-router-dom";
class Body extends React.Component {
constructor(props) {
super(props);
this.state = {
input: "",
employeeBody: this.props.employee,
}
}
getName = () => {
const { employee, add } = this.props;
const {employeeBody} = this.state;
return employee.map(name => (
<div className='item'>
<Link className="link" to={`/user/${name.id}`}>
<div onClick={() => add(name)} key={name.id}>
<img className="img"
src={`https://picsum.photos/${name.id}`}
/>
</div>
<h1 className="name2"> {name.name} </h1>
</Link>
</div>
));
};
---
getValueInput = (evt) => {
const inputValue = evt.target.value;
this.setState({ input: inputValue });
this.filterNames(inputValue);
console.log(this.state.employeeBody)
}
filterNames (inputValue) {
const { employee } = this.props;
this.setState({
employeeBody: employee.filter(item =>
item.name.includes(inputValue))
});
}
---
render() {
return (
<div>
<div className="body">
{this.getName()}
</div>
<div className='input'>
<input type="text" onChange={this.getValueInput} />
</div>
</div>
)
}
}
export default Body;
My App.js (Where i get the state by get of axios.):
import React from 'react';
import {
BrowserRouter as Router,
Route
} from "react-router-dom";
import './App.css';
import axios from 'axios';
import Body from './Body';
import User from './User';
import Header from './Header';
class AppRouter extends React.Component {
state = {
employeeCurrent: [],
employee: []
};
componentDidMount() {
axios
.get("http://127.0.0.1:3004/employee")
.then(response => this.setState({
employee: response.data
}));
}
add = name => {
this.setState(prevState => {
const copy = prevState.employeeCurrent.slice(1);
copy.push(name);
return {
employeeCurrent: copy
};
});
};
render() {
return ( <
Router >
<
div className = "router" >
<
Header / >
<
Route exact path = "/"
render = {
props => ( <
Body { ...props
}
add = {
this.add
}
employee = {
this.state.employee
}
employeeCurrent = {
this.state.employeeCurrent
}
/>
)
}
/> <
Route path = "/user/:id"
component = {
props => ( <
User { ...props
}
employee = {
this.state.employee
}
employeeCurrent = {
this.state.employeeCurrent
}
/>
)
}
/> <
/div> <
/Router>
);
}
}
export default AppRouter;
Someone would can help me ?
You should filter in the render method.
render() {
const { employee: employees } = this.props; // rename the variable {employee} to plural {employees}, it has more sense.
const { input } = this.state;
return (
<div>
<div className="body">
{employees
.filter(employee => employee.name.includes(input))
.map(employee => {
<div className='item'>
<Link className="link" to={`/user/${employee.id}`}>
<div onClick={() => add(employee)} key={employee.id}>
<img className="img"
src={`https://picsum.photos/${employee.id}`}
/>
</div>
<h1 className="name2"> {employee.name} </h1>
</Link>
</div>
})}
</div>
<div className='input'>
<input type="text" onChange={(e) => this.setState({ input: e.target.value })} />
</div>
</div>
);
}
Remember that the method includes is case sensitive, it should be lowerCase it before to compare.
P.S.: You could also create a variable / component / function and render split all the "logic" of rendering there.

Categories