1

My SquareClicker component renders a SquareGrid, which in turn contains clickable Squares:

class SquareClicker extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      grid: Array.from(Array(5).keys()).map(
        i => Array.from(Array(5).keys()).map(
          j => <Square key={((i*5)+j).toString()} onClick={this.handleClick}/>
        )
      )  
    }

    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(e) {
    console.log("A square has been clicked.");
  }

  render() {
    return (
      <div className="square-clicker">
        <SquareGrid grid={this.state.grid}/>
      </div>
      );
  };
}

class Square extends React.Component {
  render() {
    return (
      <div className={"square"} />
    );
  };
}

When I click on squares, nothing is logged to the console.

This question is similar - but as you can see, I have bound the handleClick function to the component context with this.handleClick = this.handleClick.bind(this);.

How do I make my squares clickable?

alex
  • 6,818
  • 9
  • 52
  • 103
  • Can you provide the code for your `Square ` class ? – Frosty619 Jun 19 '20 at 16:23
  • @Frosty619 Sure, I've updated my code. – alex Jun 19 '20 at 16:26
  • Storing react elements inside of component state is an anti-pattern. – trixn Jun 19 '20 at 16:26
  • @trixn Thanks. How do I avoid this anti-pattern? – alex Jun 19 '20 at 16:27
  • @alex, You're not attaching the clickHandler in your `Square` Component! – Frosty619 Jun 19 '20 at 16:28
  • Your state should only contain plain data and the render function of `SquareGrid` should take care of rendering the ``s given the plain data. – trixn Jun 19 '20 at 16:28
  • @alex You need to pass function handler as prop to child component where you have actually click happening. – harish kumar Jun 19 '20 at 16:29
  • Does this answer your question? [React Pass function to child component](https://stackoverflow.com/questions/48407785/react-pass-function-to-child-component) – harish kumar Jun 19 '20 at 16:31
  • @trixn Just as a heads-up, I fixed this "anti-pattern", but this approach has its own problems. In reality, my program has many layers of abstraction via parent/child component relationships, so in order to fix this anti-pattern, I had to decorate like five components with `squareClicker={this.props.squareClicker}`. It feels messy. – alex Jun 19 '20 at 17:32

2 Answers2

2

I misread your post initially, sorry (I got <Square> and <SquareGrid> mixed up), try this in setting your <Square> component...

j => <Square key={((i*5)+j).toString()} click={this.handleClick}/>

Then in your <Square> component, set render() as so...

render() {
    return (
        <div
            className={"square"}
            onClick={(e) => this.props.click(e)}
        />
    );
}
HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
  • What's `this.props.click`? – alex Jun 19 '20 at 16:27
  • @alex: Those are the props for the sub-class. You would have this accessible if you had a constructor in your `class Square extends React.Component`. It's typical to have constructors in components. You need to be able to pass `props` (properties) to child components. – HoldOffHunger Jun 19 '20 at 16:29
  • Ah ok, I missed `click={this.handleClick}`. Thanks, I'll try this. – alex Jun 19 '20 at 16:30
  • This worked, thanks. Do you know how I would pass parameters to the `handleClick` function? For instance, the coordinates of the square I clicked (for example given `this.props.rowNumber` and `this.props.colNumber`)? – alex Jun 19 '20 at 16:39
  • Hey, alex: Thanks for accepting! Yes, I do indeed know how to pass parms like that -- change above to `onClick={(e, parm1) => this.props.click(e, parm1)}`, and then change `handleClick(e)` to `handleClick(e, parm1)`, actually I forgot to include the `(e)` args in the answer above, so, added that just now! Thanks for noticing, hope this helps. – HoldOffHunger Jun 19 '20 at 16:45
  • Works like a charm - thanks a million! Pretty unfortunate that this sort of thing seems to *absolutely require* an ES6 arrow function. – alex Jun 19 '20 at 16:50
0

You have to pass click event to SquareGrid

// In SquareClicker

 <SquareGrid grid={this.state.grid} click={this.handleClick} />

and inside SquareGrid component, you will use click prop like below:


class SquareGrid extends React.Component {
 ...

return (
// ...you code
 <Square click={this.props.click} />
)

}

and then inside Square component pass this prop again

class Square extends React.Component {

  render() {
    return (
      <div className={"square"} onClick={this.props.click}/>
    );
  };
}
harish kumar
  • 1,732
  • 1
  • 10
  • 21