Invalid Hook call on rematchjs

157 Views Asked by At

i'll have a reactjs website where i wanted to migrate from 'rtk' to 'rematch'.

Inside my Navigation i'll have a theme switcher, which executes a dispatch on the rematch store. But I always get the error:

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:

  1. You might have mismatching versions of React and the renderer (such as React DOM)
  2. You might be breaking the Rules of Hooks
  3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

Here's my navigation

import React, { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { languages, useTypedTranslation } from '../definitions/language';
import { matchPath, useHistory } from 'react-router-dom';
import { routes } from '../definitions/routes';
import MeegoTech from '../assets/meego.svg';
import { Theme, createStyles, AppBar, Box, FormControlLabel, FormGroup, IconButton, Menu, MenuItem, Toolbar, Typography, Switch, useMediaQuery } from '@mui/material';
import { makeStyles } from '@mui/styles';
import { PersonOutline } from '@mui/icons-material';
import MenuIcon from '@mui/icons-material/Menu';
import ThemeSwitcher from '../theme/themeSwitcher';
import { useDispatch } from 'react-redux';
import { Dispatch } from '../store/configureStore';

const Navigation: React.FC = () => {

  //Authentication
  const [auth, setAuth] = React.useState(true);
  const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);

  //Translation
  const { t } = useTypedTranslation();

  const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setAuth(event.target.checked);
  };

  const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
    setAnchorEl(event.currentTarget);
  };

  const handleClose = () => {
    setAnchorEl(null);
  };

  // Get OS-level preference for dark mode
  const prefersDarkMode: boolean | undefined = useMediaQuery("(prefers-color-scheme: dark)");

//This Function throws the error
  function DispatchThemes(themeMode: boolean | undefined){
    const dispatch = useDispatch<Dispatch>()
    useEffect(() => {
      dispatch.themeModel.setDarkModeAsync(themeMode);
    }, []) 
    return <></> 
  }

  return (
    <Box sx={{ flexGrow: 1 }}>
      <FormGroup>
        <FormControlLabel
          control={
            <Switch
              color="default"
              checked={auth}
              onChange={handleChange}
              aria-label="login switch"
            />
          }
          label={auth ? 'Logout' : 'Login'}
        />
      </FormGroup>
      <AppBar position="static" style={{ backgroundColor: "rgb(255,255,255" }}>
        <Toolbar>
          <IconButton
            size="large"
            edge="start"
            color="inherit"
            aria-label="menu"
            sx={{ mr: 2 }}
          >
            <MenuIcon fontSize="large" style={{ color: "rgb(0,0,0)" }} />
          </IconButton>
          <img src={MeegoTech} style={{ height: "100px", width: "auto", marginRight: "15px" }} alt="Meego Technologies Brand" />
          <Typography variant="h6" component="div" sx={{ flexGrow: 1 }} style={{ color: "rgb(0,0,0)" }}>
            {t("layout", "meegoTech")}
          </Typography>
/* If i'll switch this switcher the error will be thrown */
          <ThemeSwitcher useOs={false} themeChanger={DispatchThemes} />
          {auth && (
            <div>
              <IconButton
                size="large"
                style={{ color: "rgb(0,0,0)" }}
                aria-label="account of current user"
                aria-controls="menu-appbar"
                aria-haspopup="true"
                onClick={handleMenu}
              >
                <PersonOutline fontSize="large" />
              </IconButton>
              <Menu
                id="menu-appbar"
                anchorEl={anchorEl}
                anchorOrigin={{
                  vertical: 'top',
                  horizontal: 'right',
                }}
                keepMounted
                transformOrigin={{
                  vertical: 'top',
                  horizontal: 'right',
                }}
                open={Boolean(anchorEl)}
                onClose={handleClose}
              >
                <MenuItem onClick={handleClose}>Profile</MenuItem>
                <MenuItem onClick={handleClose}>My account</MenuItem>
              </Menu>
            </div>
          )}
        </Toolbar>
      </AppBar>
    </Box>
  );

}

export default Navigation

Here's the code of the ThemeSwitcher:

interface ThemeSwitcherOptions {
  useDark?: boolean;
  themeChanger: (useDark?: boolean) => void;
}

const ThemeSwitcher: React.FC<ThemeSwitcherOptions> = (props) => {

  const expandedProps = {
    ...props,
    useDark: props.useDark || false,
  };

  const [theme, setTheme] = useState(expandedProps);

  const handleSwitch = (_e: any, checked: boolean) => {
    setTheme({ ...theme, useDark: checked });
    theme.themeChanger(checked);
  };

  return (
    <>
      <FormControlLabel
        style={{color:'rgb(0,0,0)'}}
        labelPlacement="end"
        label=""
        control={
          <ThemeSwitch
            checked={theme.useDark}
            onChange={handleSwitch}
          />
        }
      />
    </>
  );
}

export default ThemeSwitcher;

Does anyone has an idea why the error is thrown?

1

There are 1 best solutions below

0
On

Your DispatchThemes is a component, you even return jsx. But you pass it as themeChanger to the ThemeSwitcher component and invoke it as theme.themeChanger in an event handler. So you do not treat it as a component. Hooks are allowed to be used only in components that get rendered.

You could probably change your DispatchThemes function to

const dispatch = useDispatch<Dispatch>(); // put the dispatch outside the function

//This function should not throw an error anymore
function DispatchThemes(themeMode: boolean | undefined){
  // remove the useEffect as well
  dispatch.themeModel.setDarkModeAsync(themeMode);
}