Creating multiple components from the same onClick

Creating multiple components from the same onClick
0.0 0

#1

Hi! This is a very simple application in react! The whole point of it is that I have a button component with a onClick function. What I want it to do is everytime I click that button, it should render a animated box component on the site. So 1 click = 1 Box, so in theory if you spammed the button you would get a ton of animated boxes…

My issue is that either I would have to check if a box is showing, if its not… I can render one box but thats it, once I click the button its there until I reload and nothing happens if I continue to click on the button. Or I can just render the box without having to click on the button at all. BUT I cant seem to be able to render multiple boxes…

EDIT:
To make my question clearer, How would I render 1 AnimatedDiv Component when you click the button once? So if you click the button twice, 2 AnimatedDiv would render etc etc?

My code for the button is currently on the bool checking version as described:

import React, { Component } from 'react';
import AnimatedDiv from '../AnimatedDiv';

class Button extends React.Component
{
  state =
  {
    showComponent:false,
  };

createDivs = () =>
{
  this.setState({
    showComponent: true,
  });

}
 render() {
     return (
       <div>
       <button onClick={this.createDivs}>
       Click me and see what happens
       </button>
       {
         this.state.showComponent ?
           <AnimatedDiv />:
           null
        }
        </div>
     );
   }
 }
export default Button;


#2

Not clear your questions

 state =
  {
   
   counter:0,
  };

createDivs = () =>
{
const {counter}=this.state
  this.setState({
    counter: counter++,
  });

}
render() {
     return (
       <div>
       <button onClick={this.createDivs}>
       Click me and see what happens
       </button>
       {
          [...Array(this.state.counter)].map((k,i)=>  <AnimatedDiv key={i} />)
        }
        </div>
     );
   }

#3

Mh…just thinking, what about a counter that increment each click and then iterate ?
Something like

let createdBoxes = []
for(let el=0; el<this.counter; el++ )
                                  createdBoxes.push(<Box />)

and then render it

<div>
    {createdBoxes}
</div>

I’m just guessing, give it a try if you want! :slight_smile:


#4

Yeah I thought something like that as well! Unfortunally I cant get it to work… I’m not very good with React… :confused: This is what I tried


class Button extends Component
{
  state =
  {
    clicks: 0,
  }
handleClick = () =>
{
  this.setState({
        clicks: this.state.clicks + 1,
      })
      clicks= this.state.clicks;
}
createDivs = () =>
{
  let Divs = [];
  for(let i = 0; i<clicks; i++)
  {
    Divs.push(<AnimatedDiv />);
  }
  return Divs
}
 render() {
     return (
<div>
       <button onClick={this.handleClick}>
       </button>
       {this.createDivs}
       </div>
     )
   }
 }


#5

EDIT: It might be helpful to share AnimatedDiv's code as it may affect what you’re attempting to do here.

You can create a list of boxes. Instead of showComponent, have something like

// This code may not render correctly for your project, but it should give you the right idea.
class Button extends React.Component {
  state = {
    boxes: []
  };

  createDivs = () =>
    this.setState({
     boxes: this.state.boxes.concat(<AnimatedDiv />)
      // or {AnimatedDiv} or {new AnimatedDiv}, depends on how AnimatedDiv is written
      // I don't know, I haven't been using React as much lately, give me some slack
    });

  render() {
    return (
      <div>
        <button onClick={this.createDivs}>Click me and see what happens</button>

        <ul>
        {boxes.map(box => <li>{box}</li>)}
        </ul>
        
      </div>
    );
  }
}

export default Button;


#6

Ah wow, so many replies in few minutes :stuck_out_tongue:

What @bnoden purpose is indeed valid, i’ll post my answer corrected just for completeness:

class App extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
          counter:1
        }
       
        this.buttons =[];
    }

 
    handleTestClick = () => {
        this.setState({ counter: this.state.counter +1});
        this.buttons= [];
        for(let i=0; i<this.state.counter; i++)
            this.buttons.push(<button>{i}</button>)
    }


    render() {
        return (
            <div>
                <button onClick={this.handleTestClick}>Click Me!</button>
                <div>
                    {this.buttons}
                </div>
            </div>
        )
    }
}

#7

Btw, because you imported the named export { Component } from 'react' you can just say

class Button extends Component

instead of React.Component.

I’m sure you knew, but hey.


#8

Well I tried what you suggested, sorry it was a slow reply. I was busy! :slight_smile: What the code currently does it that it renders out the button, but nothing happens when you click it.

AnimatedDiv is basically just a styled component that returns a animated div. Can paste the code none the less.

import React from 'react';
import './index.css';

const AnimatedDiv = () =>   <div className="animate"></div>

export default AnimatedDiv;


#9

@Layer If you don’t mind, I’d like to refactor your code. I think this is pretty self-explanatory, so here’s the simple version:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      buttons: []
    };
  }

  handleTestClick = () => {
    const { buttons } = this.state;
    this.setState({
      buttons: buttons.concat(<button>{buttons.length}</button>)
    });
  };

  render() {
    return (
      <div>
        <button onClick={this.handleTestClick}>Click Me!</button>
        <div>{[...this.state.buttons]}</div>
      </div>
    );
  }
}

Here’s a more thorough version, with comments and a bonus rapid fire function:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      // Arrays are generally more suitable for state management.
      buttons: [],
      auto: 0 // This is a Boolean. For some reason I prefer 0s and 1s.
    };

    this.MAX_BUTTONS = 350; // Too many buttons can crash the browser.
  }

  handleTestClick = () => {
    const { buttons } = this.state; // Destructuring to reduce clutter in the following lines.
    const inc = buttons.length;
    this.setState({
      buttons:
        inc <= this.MAX_BUTTONS
          ? // concat returns a new array rather than changing the original array,
            // in keeping with React's preference for functional programming principles.
            // And don't need no loops
            buttons.concat(<button key={`btn${inc}`}>{inc}</button>)
            // ^Each child in an array or iterator should have a unique "key" prop.
          : buttons
    });
  };

  rapidFire = (countdown = 1500, interval = 25) => {
    // Allow about 1.5 seconds before rapid fire to avoid accidents.
    setTimeout(() => {
      this.setState({ auto: 1 });
      const turbo = setInterval(() => {
        this.state.auto ? this.handleTestClick() : clearInterval(turbo);
      }, interval);
    }, countdown);
  };

  ceaseFire = () => {
    this.setState({ auto: 0 });
  };

  render() {
    return (
      <div>
        <button
          onClick={this.handleTestClick}
          onMouseUp={this.ceaseFire}
          onMouseDown={this.rapidFire}
        >
          Click Me!
        </button>
        <div>{[...this.state.buttons]}</div>
      </div>
    );
  }
}

@oskarjarvi Okay, let’s see what you got so far. Also, maybe some of the above code will help. Either way, we’ll get it figured out within the next couple of posts.