React Drum Machine app: sending parent state down to a particular mapped component

I am working on my Drum Machine react app, and I am trying to get the state change from a click event in my parent/container component down to one of the AudioButton/drum button components that I mapped out with an external array, but I’ve had a hard time figuring this out. I don’t even know if mapping out the drum buttons are the best way to go about this project. I’d appreciate any help with trying to at least pass down the state info from the parent down to, at the very least, all of the children or, ideally, the particular child which was clicked on, although I imagine using a conditional in conjunction with array.some() would be the solution for sending the event data down to the correct button.

Current code @ codesandbox: https://codesandbox.io/s/8ylljx38p9, or

Container.js:

import React from "react";
import AudioButton from "./AudioButton";
import SoundData from "./SoundData";

class Container extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      play: false,
      SoundData
    };
    this.playSound= this.playSound.bind(this)
  }

  playSound(e){
    this.setState({play: true});
    audio.play();
    console.log(e);
  }

  render() {
    
    const ButtonComponents = SoundData.map(button => (
      <AudioButton
        key={button.id}
        id={button.id}
        clipSrc={button.clipSrc}
        clipName={button.clipName}
        onClick={this.playSound}
      />
    ));
    return (
      <div id="drum-machine">
        <main id="display">{ButtonComponents}</main>
      </div>
    );
  }
}

export default Container;

AudioButton.js:

import React from "react";

class AudioButton extends React.Component {
  constructor(props) {
    super(props);
    //this.togglePlay = this.togglePlay.bind(this);
    
  }

  audio = new Audio(this.props.clipSrc);

/*   togglePlay = () => {
    this.setState({ play: true }, 
    () => {this.state.play === true && this.audio.play(); //short circuited ternary
      //console.log(this.props.clipName);
    });
  }; */

 

  render() {
    return (
      <div>
        <button
          onClick={this.props.playSound}
          className="clip myButton"
          id={this.props.id}
        >
          {this.props.clipName}
          
        </button>
      </div>
    );
  }
}

export default AudioButton;

I think you need to pass the function as a prop, so change the onclick to a prop called playSound

const ButtonComponents = SoundData.map(button => (
      <AudioButton
        key={button.id}
        id={button.id}
        clipSrc={button.clipSrc}
        clipName={button.clipName}
        playSound={this.playSound} //makes this accessible in the child component as `this.props.playSound`
      />
    ));

Because in your AudioButton component you are already calling the function as a prop in a onclick event

render() {
    return (
      <div>
        <button
          onClick={this.props.playSound} //already calling functino as prop in onclick event
          className="clip myButton"
          id={this.props.id}
        >
          {this.props.clipName}
          
        </button>
      </div>
    );
  }

I just realized i may have failed to comprehend your question.

What exactly are you trying to pass from the parent component to the child components?

Hello, I’m afraid I don’t quite understand your problem; can you be more concrete with what is it that you’re trying to achieve (e.g. getting a sound to play but failing, or getting a unique string to display but to no avail)? And what tests are failing?

But I took a wild guess anyway; assuming what you’re trying to do is to pass this.state.play as part of the logic to display a unique string when each sound is played (cuz the current code in the sandbox doesn’t do that), you can pass the state down to the child components by doing this:

      <AudioButton
        key={button.id}
        id={button.id}
        isAudioPlaying={this.state.play}
        clipSrc={button.clipSrc}
        clipName={button.clipName}
        onClick={this.playSound}
      />

this way, each AudioButton child component will keep track of this.state.play, and you can use this state to perform the display logic.

Right now, from what I’ve seen, you’ve set up the state, have a method to change it, but the state is not used any where in your app.

Hope this can help. Cheers!