1

i am trying to add a view more or view less section in the array map method , but when i use onclick method on a button inside the map method , all the buttons are acting instead of a specific one .

class Projects extends Component{
constructor(props){
super(props);
this.state={
  projects:[
    {
      id:1,
      title:'Calculator',
      description:'A Simple Javascript Calculator application'
    },
    {
      id:2,
      title:'ColorGuess',
      description:'A Javascript application'
    },
    {
      id:3,
      title:'GamesDB',
      description:'A React application',
    },
  ],
  clicked:false
  }
   this.toggle = this.toggle.bind(this)
 }

 toggle(){
  this.setState( state => ({
  clicked:!state.clicked
  }));
 }
render(){
  return(
   <div>
    <h2>My Projects</h2>
    <div className="d-flex flex-wrap justify-content-center">
    {
      this.state.projects.map( (project, i) =>{
        return(
          <div key={i} style={{width:'40%'}}>
            <div className="card">
              {project.title}
              <hr/>
              Description : {project.description} <br/>
              <button onClick={this.toggle}>
              {this.state.clicked ? <i class="fas fa-chevron-down"></i> : <i class="fas fa-chevron-up"></i>}
              </button>
            </div>
          </div>
        );
      })
     }
    </div>
   </div>
  );
 }
}

When i click on an individual button after rendering , but all the other buttons are acting as well instead of the specific one.

i have used arrow function and also passed the index along with , but still not working , Not Sure where it went wrong .

nishant
  • 117
  • 3
  • 13
  • Well, if you have individual states for the buttons, it would seem appropriate to have an element that actually has it's own state, no? You could as easily create a component that's called `ProjectItem` that you can refactor to a functional component and use the project state to set the correct props, or to use projectitem state to see if something is toggled (it depends if toggle should behave as a single toggle, or all can be toggled) – Icepickle Feb 14 '19 at 10:05

2 Answers2

0

Hmm, so you are using one state for all the components, each time you are setting a state it going to address to clicked which is being used in this.state.clicked ? To fix this you have to maintain individual states for each button.

To fix this you need to do something like this

class Projects extends Component{
constructor(props){
super(props);
this.state={
  projects:[
    {
      id:1,
      title:'Calculator',
      description:'A Simple Javascript Calculator application'
    },
    {
      id:2,
      title:'ColorGuess',
      description:'A Javascript application'
    },
    {
      id:3,
      title:'GamesDB',
      description:'A React application',
    },
  ],
  clicked:false
  }
   this.toggle = this.toggle.bind(this)
 }

 toggle = (index) => {
   const { projects } = this.state;
   const update = projects.map((item, key) => {
    return index === key ? {
      ...item,
      clicked: !item.clicked
    } : {
      ...item,
    };
  });
  this.setState({
    projects: update
  });
 }
render(){
  const { projects } = this.state;
  const renderButtons = projects.map( (project, i) => {
    return(
      <div key={i} style={{width:'40%'}}>
        <div className="card">
          {project.title}
          <hr/>
          Description : {project.description} <br/>
          <button onClick={(i) => this.toggle(i)}>
          {project.clicked ? <i class="fas fa-chevron-down"></i> : <i class="fas fa-chevron-up"></i>}
          </button>
        </div>
      </div>
    );
  })
  return(
   <div>
    <h2>My Projects</h2>
    <div className="d-flex flex-wrap justify-content-center">
    {renderButtons}
    </div>
   </div>
  );
 }
}
Subhendu Kundu
  • 3,618
  • 6
  • 26
  • 57
0

Instead of storing a boolean value for clicked, store the id of the clicked project and use that to toggle the state of button like

class Projects extends React.Component{
  constructor(props){
    super(props);
      this.state={
        projects:[
          {
            id:1,
            title:'Calculator',
            description:'A Simple Javascript Calculator application'
          },
          {
            id:2,
            title:'ColorGuess',
            description:'A Javascript application'
          },
          {
            id:3,
            title:'GamesDB',
            description:'A React application',
          },
        ],
        clicked:false
      }
     this.toggle = this.toggle.bind(this)
   }

   toggle(id){
    this.setState( state => ({
      clicked: id === state.clicked? null: id
    }));
   }
   
   render(){
      return(
       <div>
        <h2>My Projects</h2>
        <div className="d-flex flex-wrap justify-content-center">
        {
          this.state.projects.map( (project, i) =>{
            return(
              <div key={i} style={{width:'40%'}}>
                <div className="card">
                  {project.title}
                  <hr/>
                  Description : {project.description} <br/>
                  <button type="button" onClick={() => this.toggle(project.id)}>
                  {this.state.clicked === project.id ? 'up' : 'down'}
                  </button>
                </div>
              </div>
            );
          })
         }
        </div>
       </div>
      );
    }
}

ReactDOM.render(<Projects />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<div id="root" />
Shubham Khatri
  • 270,417
  • 55
  • 406
  • 400
  • 1
    yes Working , i hadnt thought about that and just mapped one clicked to all the elements , thank you – nishant Feb 14 '19 at 07:13