Have you seen these two errors in your console?
- “TypeError destroy is not a function”
- “An effect function must not return anything besides a function, which is used for clean-up.”
As you know, useEffect is a React hook that allows us to run additional side effect code once React has updated the DOM. In cases where side effect code is asynchronous, like API calls, React allows us to clean up after these effects by returning a destroy method. (For more info, check out the React docs for using the Effect Hook ).
Based on the information from the React docs we can imply that when using the useEffect hook, we can either return a destroy function or not explicitly return any value, implicitly returning undefined. But what if we return something other than a destroy function or undefined? Let’s take a look.
For the purposes of discussion, we’ll examine a simplified example. Assume we added a useEffect hook to our application so we could log out the time on each render call. Also assume we are using an existing utility function that handles our logging, returning the argument passed in for chaining purposes.
import React, { useEffect } from "react";
const logValue = (value) => console.log("CONSOLE LOG: ", value) || value;
export default function App() {
useEffect(() => logValue(new Date()));
return (
<div className="App">
<h1>Hello World!</h1>
</div>
);
}
When we run this code we will see one of these two errors (depending on your environment):
- “TypeError destroy is not a function“
- “An effect function must not return anything besides a function, which is used for clean-up. You returned: Thu May 28 2020 …”
It appears the function passed into the useEffect hook (the “create” function) must only return a function or undefined (implicitly or explicitly). We can verify this by digging into the React source code.
effect.destroy = create();
if (__DEV__) {
const destroy = effect.destroy;
if (destroy !== undefined && typeof destroy !== 'function') {
let addendum;
if (destroy === null) {
addendum =
' You returned null. If your effect does not require clean ' +
'up, return undefined (or nothing).';
} else if (typeof destroy.then === 'function') {
addendum =
'\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +
'Instead, write the async function inside your effect ' +
'and call it immediately:\n\n' +
'useEffect(() => {\n' +
' async function fetchData() {\n' +
' // You can await here\n' +
' const response = await MyAPI.getData(someId);\n' +
' // ...\n' +
' }\n' +
' fetchData();\n' +
`}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
'Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching';
} else {
addendum = ' You returned: ' + destroy;
}
console.error(
'An effect function must not return anything besides a function, ' +
'which is used for clean-up.%s',
addendum,
);
}
}
Note, in the sample above, React uses the create function passed into the useEffect hook in order to execute the hook and create the hooks destroy method.
effect.destroy = create();
If your app is running in developer mode, React will try and help by running the new destroy function through a number of conditionals to verify you have returned either undefined or a function that does not contain a “then” method. If the value returned from the create function does not pass these conditions, React will warn you with an error. If not running in developer mode, these validations will be ignored and the application will crash once the hook framework tries to call the destroy function, which won’t actually be a function.
if (destroy !== undefined && typeof destroy !== 'function') {
let addendum;
if (destroy === null) {
addendum =
' You returned null. If your effect does not require clean ' +
'up, return undefined (or nothing).';
} else if (typeof destroy.then === 'function') {
addendum =
'\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +
'Instead, write the async function inside your effect ' +
'and call it immediately:\n\n' +
'useEffect(() => {\n' +
' async function fetchData() {\n' +
' // You can await here\n' +
' const response = await MyAPI.getData(someId);\n' +
' // ...\n' +
' }\n' +
' fetchData();\n' +
`}, [someId]); // Or [] if effect doesn't need props or state\n\n` +
'Learn more about data fetching with Hooks: https://fb.me/react-hooks-data-fetching';
} else {
addendum = ' You returned: ' + destroy;
}
console.error(
'An effect function must not return anything besides a function, ' +
'which is used for clean-up.%s',
addendum,
);
}
To summarize, if you see one of the following errors in your application, please verify your useEffect hooks return either undefined or a function without a “then” method.
- “An effect function must not return anything besides a function, which is used for clean-up.”
- “TypeError destroy is not a function”