React Custom Hook Error

Hello friends,

I am attempting to make a custom hook in React. My hook works when it simply logs something to the console, so I know my file paths are correct. But when I try to use useEffect inside my hook, I get an error of “Invalid Hook Call”. I can see in my package.json that my React and ReactDOM are both version 16.12.0.

Here is the hook I want to use. I would love to know why I am unable to use useEffect inside this functional component.

import React, { useState, useEffect } from "react";
function usePlaySound() {
  console.log("boom");

  useEffect(() => {
    console.log("test");
  });
}

export default usePlaySound;

Thanks!

Show the code where you are calling usePlaySound().

Also, useEffect has a second argument that you should be specifying, but that’s not going to be the cause of the error

The App.js code is irrelevant as my usePlaySound hook works without useEffect(). I will attach the App.js code but be warned, I am just testing some functions. The code is a mix of a previous project and some new random functions.

this hook works with my App.js

import React, { useState, useEffect } from "react";
function usePlaySound() {
  const [test, setTest] = useState([]);

  console.log("boom");


}

export default usePlaySound;

And this code doesn’t

import React, { useState, useEffect } from "react";
function usePlaySound() {
  const [bebo, setBebo] = useState([]);

  console.log("boom");

  useEffect(() => {
    console.log("test");
  }, []);
}

export default usePlaySound;

Here is my App.js (please excuse the somewhat random combination of elements)

import React, { useState, useEffect } from "react";
import usePlaySound from "./Components/usePlaySound";
import logo from "./logo.svg";
import "./App.css";

function Todo({ todo, index, completeTodo, removeTodo }) {
  return (
    <div
      style={{ textDecoration: todo.isCompleted ? "line-through" : "" }}
      className="todo"
    >
      {todo.text}
      <div>
        <button onClick={() => completeTodo(index)}>Complete</button>
        <button onClick={() => removeTodo(index)}>Remove</button>
      </div>
    </div>
  );
}

function TodoForm({ addTodo }) {
  const [value, setValue] = useState("");

  const handleSubmit = e => {
    e.preventDefault();
    if (!value) return;
    addTodo(value);
    setValue("");
  };
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        className="input"
        value={value}
        onChange={e => setValue(e.target.value)}
      />
    </form>
  );
}

function ButtonKey() {
  return <button onClick={usePlaySound}>Enter</button>;
}

function App() {
  const [todos, setTodos] = useState([
    {
      text: "Learn about react",
      isCompleted: false
    },
    { text: "Meet friend for lunch", isCompleted: false },
    { text: "Build cool Todo app", isCompleted: false }
  ]);

  const [clicked, setClicked] = useState("");

  const addTodo = text => {
    const newTodos = [...todos, { text }];
    setTodos(newTodos);
  };

  const completeTodo = index => {
    const newTodos = [...todos];
    newTodos[index].isCompleted = true;
    setTodos(newTodos);
  };

  const removeTodo = index => {
    const newTodos = [...todos];
    newTodos.splice(index, 1);
    setTodos(newTodos);
  };

  const handleUserKeyPress = event => {
    const { key, keyCode } = event;

    console.log(event);

    if (keyCode === 70) {
      removeTodo(0);
    }
  };

  useEffect(() => {
    window.addEventListener("keydown", handleUserKeyPress);

    return () => {
      window.removeEventListener("keydown", handleUserKeyPress);
    };
  });

  return (
    <div className="App">
      <audio
        id="sound1"
        src="https://freecodecampassets.s3.us-east-2.amazonaws.com/2REVKIC5.wav"
        controls
        autoPlay
      />
      <button name="pebo" onClick={usePlaySound}>
        Enter2
      </button>
      <ButtonKey />
      <div className="todo-List">
        {todos.map((todo, index) => (
          <Todo
            key={index}
            index={index}
            todo={todo}
            completeTodo={completeTodo}
            removeTodo={removeTodo}
          />
        ))}
        <TodoForm addTodo={addTodo} />
      </div>
    </div>
  );
}

export default App;

You can’t do this with hooks, this isn’t how they work:

onClick={usePlaySound}>Enter</button>

You don’t call hooks like event handlers. You register the hook at the top.of your component:

const Example = () => {
  usePlaySound();

  // rest of the code
}

That won’t actually do anything useful at the minute, but you’ll get the console.log logging

Ah, yes. Sorry for the oversight.

What I really wanted to do was use something like this Javascript, but in React:

<audio id="sound1" src="assets/donkey2.mp3" preload="auto"></audio><a class="icon fa-volume-up" onclick="document.getElementById('sound1').play();"></a>

Instead of onClick=“document.getElementById(‘sound1’).play();”
I thought I could use onClick={foo}

and function foo(){
useEffect(() => { document.getElementById(‘sound1’).play(); }, ) }

Is there anyway to use document.getElementById(‘sound1’) in React?

You can use refs: https://reactjs.org/docs/refs-and-the-dom.html

They’re a way to keep a reference to something (can be anything, but most commonly a DOM element) in the component so that you can track it, and they don’t trigger rerenders. A ref is literally just an object like this:

{ current: /* the value you're referencing */ }

So like const myRef = React.createRef(null) will create an object with current set to null. Then you can add a ref prop to the audio element and set it myRef, and myRef.current will be the DOM element when the component renders, and you can use all the DOM API methods on that

React also provides a useRef hook which you could integrate into your custom hook