简体   繁体   中英

Trouble passing a function to a stateless functional component in React

I'm creating a drum machine in React for a freeCodeCamp project. I have the buttons rendered, but cannot get the buttons to play the audio.

I've created a stateless functional component that iterates through an array of objects containing the the audio url and a few other details, creating a keyboard to play the sounds. The function to play the audio is in the app component, and I'm passing the function to the Keyboard as props.

I'm getting an object error in the console, and can't figure out where it's coming from. The CodePen is here ( https://codepen.io/cpmcclure/pen/qBXGNpw ), and I've copied the code below as well. Any thoughts would help. Thanks in advance!

 class App extends React.Component { constructor(props) { super(props); this.playSound = this.playSound.bind(this); } playSound = (key) => { const audio = document.getElementById(key); audio.currentTime = 0; audio.play(); } render() { return( <Keyboard play={this.playSound}/> ) } } const Keyboard = ({playSound}) => { return TR66.map(sound => { return (<button class="drum-pad" id={sound.id} onClick={() => playSound(sound.key)}> <audio class="clip" id={sound.key} src={sound.url}/> <div>{sound.key}</div> </button>) }); } const TR66 = [ { keyCode: 81, key: 'Q', id: 'Bass Drum', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/15[kb]66-bd-01.wav.mp3' }, { keyCode: 87, key: 'W', id: 'Snare', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/14[kb]66-sd-01.wav.mp3' }, { keyCode: 69, key: 'E', id: 'Hi Hat 1', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/6[kb]66-hh-01-or.wav.mp3' }, { keyCode: 65, key: 'A', id: 'Hi Hat 2', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/5[kb]66-hh-08.wav.mp3' }, { keyCode: 83, key: 'S', id: 'Hi Hat Open 1', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/33[kb]66-hho-01-or.wav.mp3' }, { keyCode: 68, key: 'D', id: 'Hi Hat Open 2', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/37[kb]66-hho-04.wav.mp3' }, { keyCode: 90, key: 'Z', id: 'Wood Block Low', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/26[kb]66-per-08.wav.mp3' }, { keyCode: 88, key: 'X', id: 'Wood Block High', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/12[kb]66-per-03.wav.mp3' }, { keyCode: 67, key: 'C', id: 'Rim', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/11[kb]66-rim-01.wav.mp3' } ] ReactDOM.render(<App />, document.getElementById("drum-machine"))
 button { width: 5rem; height: 5rem; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="drum-machine"> </div>

There appears to be a mistake in naming: the component function is const Keyboard = ({playSound}) => { but it's created with <Keyboard play={this.playSound}/> . The correct match would be <Keyboard playSound={this.playSound}/> .

Also, class should be className and all array child elements need to have unique keys.

I suggest using functional components throughout. No state is necessary, and if it was, you can use the useState hook .

I recommend attaching the audio objects to your drum kit elements. Keeping the kit data self-contained in its own data structure alleviates the burden on your components of rendering <audio> elements, then subsequently finding them by id just to play a clip. There's no need to go through the DOM -- instead, you can simply say kit[i].audio.play() ( kit is a generic kit that we fill with the TR66 set here) once you've set kit[i] = new Audio(kit[i].url) .

You can use a document listener for the key triggers, if desired, or attach that listener to the drum machine component alone. This illustrates that having the audio objects in the kit makes it easy to play them from multiple places, although I realize it's jumping ahead a bit on your current code. Remove the useEffect if you don't need this.

 const App = () => <DrumMachine kit={TR66} />; const DrumPad = ({text, onPlay}) => ( <button className="drum-pad" onClick={onPlay}> <div>{text}</div> </button> ); const DrumMachine = ({kit}) => { const playAudio = audio => { audio.currentTime = 0; audio.play(); }; React.useEffect(() => { const listener = e => { const drum = kit.find(({key}) => key.toLowerCase() === e.key.toLowerCase() ); drum && playAudio(drum.audio); }; document.addEventListener("keydown", listener); return () => document.removeEventListener("keydown", listener); }, []); return ( <div> {kit.map(({key, id, audio}) => <DrumPad key={id} text={key} onPlay={() => playAudio(audio)} /> )} </div> ); }; const TR66 = [ { keyCode: 81, key: 'Q', id: 'Bass Drum', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/15[kb]66-bd-01.wav.mp3' }, { keyCode: 87, key: 'W', id: 'Snare', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/14[kb]66-sd-01.wav.mp3' }, { keyCode: 69, key: 'E', id: 'Hi Hat 1', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/6[kb]66-hh-01-or.wav.mp3' }, { keyCode: 65, key: 'A', id: 'Hi Hat 2', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/5[kb]66-hh-08.wav.mp3' }, { keyCode: 83, key: 'S', id: 'Hi Hat Open 1', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/33[kb]66-hho-01-or.wav.mp3' }, { keyCode: 68, key: 'D', id: 'Hi Hat Open 2', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/37[kb]66-hho-04.wav.mp3' }, { keyCode: 90, key: 'Z', id: 'Wood Block Low', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/26[kb]66-per-08.wav.mp3' }, { keyCode: 88, key: 'X', id: 'Wood Block High', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/12[kb]66-per-03.wav.mp3' }, { keyCode: 67, key: 'C', id: 'Rim', url: 'https://sampleswap.org/samples-ghost/DRUMS%20(FULL%20KITS)/DRUM%20MACHINES/Roland%20TR-66/11[kb]66-rim-01.wav.mp3' } ]; TR66.forEach(({url}, i) => { TR66[i].audio = new Audio(url); }); ReactDOM.render(<App />, document.getElementById("app"));
 button { width: 5rem; height: 5rem; }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script> <div id="app"></div>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM