denoland / fresh

The next-gen web framework.
https://fresh.deno.dev
MIT License
12.15k stars 619 forks source link

Hook is already inside component, yet error `Hook can only be invoked from render methods` still occurs #2403

Closed ooker777 closed 4 months ago

ooker777 commented 4 months ago

In islands/test.tsx:

export default function Test() {
  return (
    <form onSubmit={(e: FormDataEvent) =>    
      useEffect(()=> {
        fetch('https://example.com').then(console.log).catch(console.error) 
      },[e.currentTarget])}
    >
      <input/>
      <button type="submit">submit</button>
    </form>
  ) 
}

In routes/index.tsx:


import Test from "../islands/test.tsx";
export default function App() {
  return (
    <Test />
  );
} 

Do you have any idea why this happens?

marvinhagemeister commented 4 months ago

The main point of hooks is to attach logic to the component lifecycle like when it renders, when it's created or when it's destroyed. Using hooks for anything else is not necessary. An event handler is not part of the component lifecycle. It's just a function that the browser calls in response to some event. It runs completely separate from the component lifecycle

Hooks can only be used inside a component context, which is why this error is thrown. But here the useEffect is called inside an event handler instead. Event handlers are not part of the component lifecycle.

In your case you don't even need a hook in there. You can just call the fetch directly.

  export default function Test() {
    return (
     <form onSubmit={(e: FormDataEvent) =>    
-       useEffect(()=> {
-         fetch('https://example.com/').then(console.log).catch(console.error) 
-       },[e.currentTarget])}
     <form onSubmit={(e: FormDataEvent) => 
+         fetch('https://example.com/').then(console.log).catch(console.error) 
+       }
      >
        <input/>
        <button type="submit">submit</button>
      </form>
    ) 
  }