Lifting up the state

I’m having trouble passing the final test. I was recommended to lift up the state and tried reading about and watching some videos, but I can’t make it work with my code. Could anyone explain how exactly I do this with my code? Whenever I remove the state from MusicPads everything stops rendering.

pen

You’re going to have to more specific about what problem you’re having.

Right off the bat, I’m not sure what this line is doing:

      {SoundName}

You’re trying to show the component definition on the screen?

So, you have an app with the architecture of:

DrumMachine --|
              |-- SoundName
              |-- MusicPads

OK, that makes sense. The problem (I’m assuming that you’re running into is sharing information. Usually (until you get into redux or something like that) you store your app state in the most common ancestor. SoundName and MusicPads both need access to state, so that means that you need to store state in DrumMachine. Then you can pass that state in as props to the components that need it and pass in callback functions to the components that need to alter it.

This gets a little confusing. I once made a pen to help explain this. And this gets really complicated with big apps - that’s why people invented things like redux and context - but you can worry about those later.

I’m trying to display the state . I’m not sure how to store the state in drummachine since I keep running into errors when I remove the state from DrumPads.

I need more to go on than this.

But if I were to guess, remember that then you won’t have state in DrumPads. You will pass in the state that you need as:

            <MusicPads
              currentSound={this.state.currentSound{
              keys={sound.keyCode}
              keyName={sound.id}
              text={sound.key}
              key={idx}
              audio={sound.url}
            />

And then MusicPads can access that on its props.

I don’t understand why SoundName doesn’t work in DrumMachine. I expect that once I click a MusicPad the ID should be displayed using the state.

I would highly recommend using CodeSandbox and not Codepen. You will get much better error messages which is a huge help when debugging. Codepen often just swallows the errors and gives little to no indication of what is wrong.

BTW, here is the other thread just for cross reference

Thanks I’m gonna give that a shot.

It’s actually a lot better than using codepen. I’ve been messing around with it and I’ve figured out how to lift the state I believe. I have run into a new problem which is “Cannot read property ‘current’ of undefined” for handleChange. I’ll keep at it.

Errors like this in my experience often point you to the part of the code referencing a mistake you made earlier. Look at the function / var in question and sniff back and forward in your code looking for a typo or syntax error or other mistakes.

I did end up finding an error, it has changed to “Cannot read property ‘play’ of null”.

So you should go through your code and find the logic for play and figure out why it is returning null. It sounds like you initiated a ‘play’ but may have an issue in your onChange or onClick to update the state.

this.audio.current.play();
I know it’s this line, but I’m not sure how to fix it.

Can you link to your Codesandbox?

I’m guessing the issue is with the ref being pass down from the parent. I’m not sure using refs for this project is really the best approach. I have seen people have issues with this before, especially when the buttons are in a map and you just end up with the last button in the map being the ref. I know there are ways around it but haven’t really looked too much at it.

It may be easier just to get to the elements using normal browser APIs using the id and/or text from the elements for the selectors.

Here it is
sandbox

Your ref is just the URL string from the object. The ref is supposed to be the element audios={this.audio}. The problem is you will just get a ref to the last button in the map.

But even if you do fix that, I would suggest you log out this.audio.current and have a look at the src attribute on the audio element.

playSound = () => {
  console.log(this.audio.current);
  // this.audio.current.play();
};

So it’s all identical audio elements. Honestly, I have no idea why this is happening. I don’t understand how there’s nine different text, but only the last ref.

Honestly, I would suggest you use the event in the handler function to get to the elements, I think it’s just easier. Sure it’s some plain browser web API stuff mixed in with React, but that is not the end of the world.

To get the url? Sorry, I’ve been trying to figure this out all morning and I’m kind of lost in it all.

First, you need to look at the src on the audio element and make sure it is a string and not an object (not sure if you noticed this or not).

You would use the event.target in the handler function to get to the click target, then you can use plain API methods, like getting the id of the element, getting to the child element, getting the text content (might be useful considering the audio id and button text is the same) using e.g. getElementById to grab the audio element and calling .play() straight on the audio element.

If you do not know about plain JS DOM stuff now is a good time to learn. Sure it isn’t really the “React way” of doing things (for the most) but I would recommend knowing about plain JS DOM manipulation no matter what.

1 Like

Your components are re-rendering twice with every click. Reference is pointed to MusicPads component, however with each iteration properties object Ref gets updated with new value that is why when you press button there is always the last sound playing (last assignment of ref is to last button). In other words Ref is a reference to an object passed down to child components. It can only have one reference, so it does not create new Ref for each component, it is using the same object (reference to that Object). So the value of Ref gets overwritten with each iteration of map() function called on properties array.
Since you are not using hooks, maybe try to implement shouldComponentUpdate to prevent unnecessary renders.