Aditya Dhiman
Aditya Dhiman

Reputation: 19

Could not add multiple counters in react

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.

Upvotes: 0

Views: 1550

Answers (5)

Kishor
Kishor

Reputation: 2677

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;

Upvotes: 0

Drew Reese
Drew Reese

Reputation: 202706

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>
    );
  }
}

Edit frosty-monad-bc71e

* React render lifecycle function can render arrays.

Upvotes: 0

kyun
kyun

Reputation: 10264

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>

Upvotes: 0

Joseph D.
Joseph D.

Reputation: 12174

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

Upvotes: 2

P4uB0rd4
P4uB0rd4

Reputation: 175

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.

Upvotes: 0

Related Questions