React not rendenring buttons to the screen

Tell us what’s happening:
I am trying to render letters to the screen (Q,W,E,A, etc). React will not reder letters. Can you please help me to figure out why.

Your code so far

const beats = [
  {
    keyCode: 81,
    keyTrigger: "Q",
    id: "Heater-1",
    url: "'https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3'"
  },
  {
    keyCode: 87,
    keyTrigger: "W",
    id: "Heater-2",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-2.mp3"
  },
  {
    keyCode: 69,
    keyTrigger: "E",
    id: "Heater3",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-3.mp3"
  },
  {
    keyCode: 65,
    keyTrigger: "A",
    id: "Heater4",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-4_1.mp3"
  },
  {
    keyCode: 83,
    keyTrigger: "S",
    id: "",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-6.mp3"
  },
  {
    keyCode: 68,
    keyTrigger: "D",
    id: "Open-HH",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Dsc_Oh.mp3"
  },
  {
    keyCode: 90,
    keyTrigger: "Z",
    id: "Kick-n'-Hat",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Kick_n_Hat.mp3"
  },

  {
    keyCode: 88,
    keyTrigger: "X",
    id: "Kick",
    url: "https://s3.amazonaws.com/freecodecamp/drums/RP4_KICK_1.mp3"
  },
  {
    keyCode: 67,
    keyTrigger: "C",
    id: "Closed-HH",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3"
  }
];

//key styles

const padInactive = {
  backgroundColor: "#024201d",
  boxShadow: "3px 6px 3px black"
  
};

//React component

class Drum extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      padStyle: padInactive
    };

    //bind methods here
    // this.handelKeyPress=this.handleKeyPress.bind(this);
  }
  //declare methods here

  /*componentDidMount() {
    doucment.addEventListener("keydown", this.handleKeyPress);
  }

handleKeyPress(event) {
  if(event.keyCode===this.props.keyCode){
    this.playNote();
  }
}
  playNote() {
    sound.play();
  } */

  render() {
    return (
      <div
             className="drum-pad"
             id={this.props.clipId}
             state={this.state.padStyle}
             onClick={this.playsound}   
             >
           
      {this.props.keyTrigger}
      
             </div>
    
    );
  }
}

ReactDOM.render(<Drum />, document.getElementById("App"));

My CSS code:

*root {
  
}
.drum-pad {
  border-style: solid;
  color: green;
  width: 50vw;
  height: 50vh;
  margin-top: 10em;
  margin-left: 10em;
  display:flex;
  flex-direction:row;
  align-items: center;
  justify-content:center;
  font-size: 0.8em
 }

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36

Challenge: Build a Drum Machine

Link to the challenge:

You haven’t passed in a prop to Drum for it to work with.

First of all, as discussed in a previous thread, this:

{this.props.keyTrigger}

won’t do anything because this.props is an empty object so this.props.keyTrigger is undefined.

Logging out props, I get:

Drum {props: {…}, context: {…}, refs: {…}, updater: {…}, state: {…}, …}
context: {}
props: {}
refs: {}
state: {padStyle: {…}}
updater: {isMounted: ƒ, enqueueSetState: ƒ, enqueueReplaceState: ƒ, enqueueForceUpdate: ƒ}
_reactInternalFiber: Fj {tag: 1, key: null, stateNode: Drum, elementType: ƒ, type: ƒ, …}

But you can access them directly since they are in scope:

        {beats[0].keyTrigger}
        <br/>
        <br/>
        <button>{beats[1].keyTrigger}</button>

But the way you are trying to access them, as if keyTrigger is a unique value for this component leads me to think that this is dealing with each button. I think it would be good to create a React component to create each button. Then you could pass in the values from each. You could have something like:

{
  buttons.map(MyButton)
}

in the Drum component and it will make each of your buttons, passing in the values from the array one by one.

1 Like

If you really, really, really want those beats on props, you can pass them in to the root component like this:

ReactDOM.render(<Drum beats={beats} />, document.getElementById("App"));

But, I wouldn’t do that - that’s just weird. You could also keep them on state:

    this.state = {
      beats: beats,
      padStyle: padInactive
    };

That would give you this.state.beats.

But to be honest, since these values are not going to change while the app is running, they are effectively a constant. I would just access that constant directly. I would however put the variable name into SCREAMING_SNAKE_CASE as “BEATS”. That is a common convention, but it is not required.

I am terribly sorry to bother you again. It just that I am still stuck (never been stuck this long) and simply don’t know how to move on.
I want my components to render keyTrigger values to the screen (Q,W,C, etc.) Once I manage that, I beleive I will be able to apply the same principle to render ids, styles,etc.

I put ‘beats’ as a state of my Drum component, as you suggested. I passed props to MyKeys component and mapped it. It all appears to be fine. When I do the console.log, it prints string characters “Q”, “W”, “E”…as it should.

However, when I try to render these characters in MyKeys component, browser console gives an error message: “Uncaught Invariant Violation” and “MyKeys.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.”
These are string characters, placed within div element. How can they be of invalid type?
I am trying to solve these without taking shortcuts, in order to learn React. I think I’ve grasped the concept, however, about every step of the way there is an error message. Most of them I resolve…eventually, but this one is confusing.

In the solution provided, I see they have mapped index as well, and used it to extract characters (keyTrigger={padBankArr[i].keyTrigger}.
I tried that and got an error message. My mapping {obj.keyTrigger} does print characters in console.log, but does not render.

Do you have an advice for me (other than to abandon coding and do something else :smiley: )?

Thanks.

const beatsOne = [
  {
    keyCode: 81,
    keyTrigger: "Q",
    id: "Heater-1",
    url: "'https://s3.amazonaws.com/freecodecamp/drums/Heater-1.mp3'"
  },
  {
    keyCode: 87,
    keyTrigger: "W",
    id: "Heater-2",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-2.mp3"
  },
  {
    keyCode: 69,
    keyTrigger: "E",
    id: "Heater3",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-3.mp3"
  },
  {
    keyCode: 65,
    keyTrigger: "A",
    id: "Heater4",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-4_1.mp3"
  },
  {
    keyCode: 83,
    keyTrigger: "S",
    id: "",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Heater-6.mp3"
  },
  {
    keyCode: 68,
    keyTrigger: "D",
    id: "Open-HH",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Dsc_Oh.mp3"
  },
  {
    keyCode: 90,
    keyTrigger: "Z",
    id: "Kick-n'-Hat",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Kick_n_Hat.mp3"
  },

  {
    keyCode: 88,
    keyTrigger: "X",
    id: "Kick",
    url: "https://s3.amazonaws.com/freecodecamp/drums/RP4_KICK_1.mp3"
  },
  {
    keyCode: 67,
    keyTrigger: "C",
    id: "Closed-HH",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Cev_H2.mp3"
  }
];

const beatsTwo = [
  {
    keyCode: 81,
    keyTrigger: "Q",
    id: "Chord-1",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Chord_1.mp3"
  },
  {
    keyCode: 87,
    keyTrigger: "W",
    id: "Chord-2",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Chord_2.mp3"
  },
  {
    keyCode: 69,
    keyTrigger: "E",
    id: "Chord-3",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Chord_3.mp3"
  },
  {
    keyCode: 65,
    keyTrigger: "A",
    id: "Shaker",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Give_us_a_light.mp3"
  },
  {
    keyCode: 83,
    keyTrigger: "S",
    id: "Open-HH",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Dry_Ohh.mp3"
  },
  {
    keyCode: 68,
    keyTrigger: "D",
    id: "Closed-HH",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Bld_H1.mp3"
  },
  {
    keyCode: 90,
    keyTrigger: "Z",
    id: "Punchy-Kick",
    url: "https://s3.amazonaws.com/freecodecamp/drums/punchy_kick_1.mp3"
  },

  {
    keyCode: 88,
    keyTrigger: "X",
    id: "Side-Stick",
    url: "https://s3.amazonaws.com/freecodecamp/drums/side_stick_1.mp3"
  },
  {
    keyCode: 67,
    keyTrigger: "C",
    id: "Snare",
    url: "https://s3.amazonaws.com/freecodecamp/drums/Brk_Snr.mp3"
  }
];

//key styles

const padInactive = {
  backgroundColor: "#024201d",
  boxShadow: "3px 6px 3px black"
};

//component to create button
class MyKeys extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    // console.log(this.props.beats[1].keyTrigger)
    let keys;
    keys = this.props.beats.map((obj) => {
      console.log(obj.keyTrigger);
      return (
        <div>
          <div>test1</div>
          <div>{obj.keyTrigger}</div>
          <div>test2</div>
        </div>
      );
    });
  }
}

class Drum extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      padStyle: padInactive,
      beats: beatsOne
    };

    //bind methods here
  }
  //declare methods here

  render() {
    return (
      <div>
    <MyKeys beats={this.state.beats} />;
    <div>testdrum</div>
      </div>
      );
    
  }
}
ReactDOM.render(<Drum />, document.getElementById("App"));

I am terribly sorry to bother you again. It just that I am still stuck (never been stuck this long) and simply don’t know how to move on.

We’ve all been there. Relax, you’re among friends.

A couple observations:


    <MyKeys beats={this.state.beats} />;

What is that semicolon doing there? That is invalid JSX.


However, when I try to render these characters in MyKeys component, browser console gives an error message: “Uncaught Invariant Violation” and “MyKeys.render(): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.”

Yes, every React component must return valid JSX, or at least return null (to tell it not to render).

What is this returning?

  render() {
    // console.log(this.props.beats[1].keyTrigger)
    let keys;
    keys = this.props.beats.map((obj) => {
      console.log(obj.keyTrigger);
      return (
        <div>
          <div>test1</div>
          <div>{obj.keyTrigger}</div>
          <div>test2</div>
        </div>
      );
    });
  }

It returns nothing, which means it is returning undefined. True, your callback function is returning something, but that is being consumed by map. Your render method is returning nothing.


When I fix those two things above, your screen renders for me.

Here are a few other comments:


  constructor(props) {
    super(props);
  }

This is the default constructor. If this is all you need, you can leave it out of the code.

class Drum extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      padStyle: padInactive,
      beats: beatsOne
    };

    //bind methods here
  }
  // ...

Again, you really don’t need that constructor. You can just set state directly as a class property:

class Drum extends React.Component {
  state = {
    padStyle: padInactive,
    beats: beatsOne
  };
  // ...

Also, if you use arrow functions you also don’t need to bind this because an arrow function just inherits the this from it’s caller, in this case the class, so:

class MyClass extends React.Component {
  myMethod() {
    return 'something';
  }
  // ...

can be replaced by:

class MyClass extends React.Component {
  myMethod = () => {
    return 'something';
  }
  // ...

and there is no need for binding.


Also, I would probably make those functional components, especially MyKeys. Really, if you don’t need state or lifecycle methods, then you don’t need a class. And now even a class can do those (but it’s a little weird until you get used to it.) But you may not have encountered those yet. Don’t worry about it, I’m just mentioning it. Keep using classes if it is more comfortable, but I almost never write class components. But they’re still important to learn because you will encounter them.

Yes, I know you were taught to write components a certain way. But the patterns in React have moved on since these tutorials were written. I’m showing you some simpler ways.


Offhand, I’d probably make your “beats” and array of arrays rather than two arrays. Then you could map through the outer array and then map through each of the inner arrays. But that’s not a big deal if that makes things more confusing.


Do you have an advice for me (other than to abandon coding and do something else :smiley: )?

Dude, this is tough stuff. HTML/JS/CSS and confusing enough and then React is a whole different way of thinking. And Redux is a whole different way of thinking. There will be others. I assure you, you are no worse off than I was when I was where you are. And now I do it for a living. Cut yourself some slack.

1 Like

Thank you!

It will take me a while to digest all of what you wrote.

It makes a lot of sense to me to use functional components rather than statefull class components (wherever possible). They mention in lessons that less stateful components we use, more stable system is.

I’ve tried using functional components, ended up with lots of errors and returned to use what I think I know better.

Definitely, it is better to be up to date with the approach, so I will be happy to rewrite the whole thing…just need to catch my breath :sweat_smile:

Your feedback is rich with info. Thank you for taking the time to write it.

Then stick with that for now. If you master class components, then functional components will be easy.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.