SushiCode
SushiCode

Reputation: 97

Event handler throws invalid hook call error

I am learning React and trying to implement a simple onClick button in a child Component only to get an "Invalid hook call". I've gutted a lot of my program to isolate just the problem.

Parent Component:

import React, { useState } from "react";
import Menubar from '../Menubar/Menubar'

function SortingVisualizer() {
    const [arr, setArr] = useState([]);

    function newArray() {
        const tempArr = [];
        for(let i = 0; i < 100; i++) {
            tempArr.push(Math.floor(Math.random() * 100) + 5)
        }
        setArr(tempArr);
    }

    return (
        <div id="main-container">

            <Menubar
                data={arr}
            />

        </div>
    )
}

export default SortingVisualizer;

Child Component in another module:

import React from "react";

import newArray from '../SortingVisualizer/SortingVisualizer'
import bubbleSort from '../algorithms/bubbleSort'

function Menubar(props) {
return(
    <div id="menubar-container">
        <button id="new-array-button" onClick={() => newArray()}>New Array</button> // Invalid hook error
        <button id="bubble-sort-button" onClick={() => bubbleSort(props.data)}>Bubble Sort</button> // Works fine
    </div>
    )
}

export default Menubar;

When I run "npm ls react-dom", I only get a single version returned which should mean I do not have mismatching version problems:

`-- [email protected] 

I suspect that I am breaking some hook rules but not sure which one. If I had to guess, based on another problem I saw, I think this has to do with the fact that hooks can only be used at the top level. I think that somehow my onClick for newArray is misusing that hook rule.

But something else I don't get is...shouldn't "import newArray..." be enough to use a function from another module? Isn't that how it works in plain javascript where one module can use the function of another module simply by importing/exporting?

Upvotes: 0

Views: 140

Answers (3)

Daphaz
Daphaz

Reputation: 506

I'm not sure if this is the best solution but you can create a hook too

// useArray.js

import {useState} from "react"

const useArray = () => {
  const [arr,setArray] = useState([])

  function newArray() {
    const tempArr = [];
    for(let i = 0; i < 100; i++) {
      tempArr.push(Math.floor(Math.random() * 100) + 5)
    }
    setArr(tempArr);
  }

  return {
   newArray,
   arr
  }
}

export default useArray

even if in your example I don't understand why not to put your state in your child component since your parent doesn't use it

//Child component
import React from "react";

import bubbleSort from '../algorithms/bubbleSort'

function Menubar() {

const [arr, setArr] = useState([]);

const newArray = () => {
   // ...
}

return(
    <div id="menubar-container">
        <button id="new-array-button" onClick={newArray}>New Array</button>
        <button id="bubble-sort-button" onClick={() => bubbleSort(arr)}>Bubble Sort</button>
    </div>
    )
}

export default Menubar;

Upvotes: 0

HichamELBSI
HichamELBSI

Reputation: 1719

You can't import a function like that. You have to pass the function to the children component.

In the parent component

// SortingVisualizer.js

export const SortingVisualizer = () => {
    const [arr, setArr] = useState([]);
    const newArray = () => {
      // ...
    }

    return (
      <div id="main-container">
        <Menubar data={arr} newArray={newArray} />
      </div>
    )
}

In the children component

// import newArray from '../SortingVisualizer/SortingVisualizer'; Remove that line
import bubbleSort from '../algorithms/bubbleSort'

const Menubar = ({ newArray }) => {
return(
    <div id="menubar-container">
        <button id="new-array-button" onClick={() => newArray()}>New Array</button>
        <button id="bubble-sort-button" onClick={() => bubbleSort(props.data)}>Bubble Sort</button> // Works fine
    </div>
    )
}

Upvotes: 2

VersifiXion
VersifiXion

Reputation: 2282

To pass the function to the child component, you can pass it as a props

<Menubar
  data={arr}
  newArray={newArray}
/>

Then in the child component, you call the function from the props

const { newArray } = props;
return (
    <div id="menubar-container">
    <button id="new-array-button" onClick={newArray}>New Array</button>

Upvotes: 0

Related Questions