Reactjs 无法在组件中使用挂钩

Reactjs 无法在组件中使用挂钩,reactjs,react-hooks,notistack,Reactjs,React Hooks,Notistack,我正在尝试使用一个钩子,但在使用来自notistack的useSnackbar钩子时出现以下错误 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 th

我正在尝试使用一个钩子,但在使用来自notistack的useSnackbar钩子时出现以下错误

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
我的App.js

 <SnackbarProvider
      anchorOrigin={{
        vertical: 'top',
        horizontal: 'center',
      }}
    >
      <App />
 </SnackbarProvider>
如果我调用demo.js中的钩子并将其作为参数传入,如下面所示,它是有效的。有什么区别?为什么我不能在snackbar.js中使用useSnackbar()钩子

const Demo = props => {
    const showSnackBar = (message, severity) => {
      SnackBar(enqueueSnackbar, closeSnackbar, message, severity)
    }
}

钩子用于React组件,这些组件是涂有语法糖的JSX元素

目前,您正在使用
useSnackbar()
hook-inSnackBar.js

为了工作,SnackBar.js必须是React组件

要检查的东西

  • 如果已从组件范围内的
    导入React,“React”
  • 如果有
    return
    JSX标记供组件渲染
  • 对于你的情况

    • 您的
      SnackBar.js
      不是一个组件,因为它不会返回任何内容
    • 您的
      demo.js
      之所以有效,是因为它是一个组件,并且已经调用了钩子,然后将结果传递给子函数
    改变

    const SnackBar=(消息,严重性)=>{}

    const SnackBar=({message,severity})=>{}

    你也必须返回一些加价


    返回一些东西

    简单的方法 在应用程序启动时,将enqueueSnackbar和closeSnackbar存储在some类变量中,并在应用程序中的任何位置使用。 按照下面的步骤进行操作,

    1.在Routes.js文件中存储enqueueSnackbar和closeSnackbar to class变量。

    import React, { Component, useEffect, useState } from 'react';
    import {Switch,Route, Redirect, useLocation} from 'react-router-dom';
    import AppLayout from '../components/common/AppLayout';
    import PrivateRoute from '../components/common/PrivateRoute';
    import DashboardRoutes from './DashboardRoutes';
    import AuthRoutes from './AuthRoutes';
    import Auth from '../services/https/Auth';
    import store from '../store';
    import { setCurrentUser } from '../store/user/action';
    import MySpinner from '../components/common/MySpinner';
    import { SnackbarProvider, useSnackbar } from "notistack";
    import SnackbarUtils from '../utils/SnackbarUtils';
    
    const Routes = () => {
        const location = useLocation()
        const [authLoading,setAuthLoading] = useState(true)
    
        //1. UseHooks to get enqueueSnackbar, closeSnackbar
        const { enqueueSnackbar, closeSnackbar } = useSnackbar();
       
        useEffect(()=>{
    
        //2. Store both  enqueueSnackbar & closeSnackbar to class variables
            SnackbarUtils.setSnackBar(enqueueSnackbar,closeSnackbar)
            const currentUser = Auth.getCurrentUser()
            store.dispatch(setCurrentUser(currentUser))
            setAuthLoading(false)
        },[])
        if(authLoading){
            return(
                <MySpinner title="Authenticating..."/>
            )
        }
        return ( 
            <AppLayout 
            noLayout={location.pathname=="/auth/login"||location.pathname=="/auth/register"}
            >
                <div>
                    <Switch>
                        <Redirect from="/" to="/auth" exact/>
                        <PrivateRoute redirectWithAuthCheck={true}  path = "/auth" component={AuthRoutes}/>
                        <PrivateRoute path = "/dashboard" component={DashboardRoutes}/>
                        <Redirect  to="/auth"/>
                    </Switch>
                </div>
            </AppLayout>
         );
    }
     
    export default Routes;
    
    <button onClick={()=>{
               SnackbarUtils.success("Hello")
            }}>Show</button>
    
    3.现在只需导入SnackbarUtils并在应用程序中的任意位置使用snackbar,如下所示。

    import React, { Component, useEffect, useState } from 'react';
    import {Switch,Route, Redirect, useLocation} from 'react-router-dom';
    import AppLayout from '../components/common/AppLayout';
    import PrivateRoute from '../components/common/PrivateRoute';
    import DashboardRoutes from './DashboardRoutes';
    import AuthRoutes from './AuthRoutes';
    import Auth from '../services/https/Auth';
    import store from '../store';
    import { setCurrentUser } from '../store/user/action';
    import MySpinner from '../components/common/MySpinner';
    import { SnackbarProvider, useSnackbar } from "notistack";
    import SnackbarUtils from '../utils/SnackbarUtils';
    
    const Routes = () => {
        const location = useLocation()
        const [authLoading,setAuthLoading] = useState(true)
    
        //1. UseHooks to get enqueueSnackbar, closeSnackbar
        const { enqueueSnackbar, closeSnackbar } = useSnackbar();
       
        useEffect(()=>{
    
        //2. Store both  enqueueSnackbar & closeSnackbar to class variables
            SnackbarUtils.setSnackBar(enqueueSnackbar,closeSnackbar)
            const currentUser = Auth.getCurrentUser()
            store.dispatch(setCurrentUser(currentUser))
            setAuthLoading(false)
        },[])
        if(authLoading){
            return(
                <MySpinner title="Authenticating..."/>
            )
        }
        return ( 
            <AppLayout 
            noLayout={location.pathname=="/auth/login"||location.pathname=="/auth/register"}
            >
                <div>
                    <Switch>
                        <Redirect from="/" to="/auth" exact/>
                        <PrivateRoute redirectWithAuthCheck={true}  path = "/auth" component={AuthRoutes}/>
                        <PrivateRoute path = "/dashboard" component={DashboardRoutes}/>
                        <Redirect  to="/auth"/>
                    </Switch>
                </div>
            </AppLayout>
         );
    }
     
    export default Routes;
    
    <button onClick={()=>{
               SnackbarUtils.success("Hello")
            }}>Show</button>
    
    {
    SnackbarUtils.success(“你好”)
    }}>展示
    

    您也可以在非react组件文件中使用snackbar

    更新:无法在snackbar.js中调用useSnackbar()的原因是snackbar.js不是功能组件。hooks()的强大规则规定,您只能从以下位置调用hooks:1)功能组件主体2)其他自定义hooks。我建议进行重构,就像您在demo.js中首先调用钩子一样,然后将响应对象(以及enqueueSnackbar函数)传递给任何其他函数

    以前的答复:

    Prabin的解决方案感觉有点老套,但我想不出更好的解决方案来支持超级易于使用的全球Snackbar

    对任何人来说
    “TypeError:无法解构'Object(…)(…)'的属性'enqueueSnackbar',因为它未定义”


    这种情况之所以发生在我身上,是因为我在我的主app.js(或路由器)组件中使用了useSnackbar(),顺便说一句,该组件与初始化组件的位置相同。不能在声明上下文提供程序的同一组件中使用它,它必须是子元素。因此,我创建了一个名为Snackbar的空组件,用于将enqueueSnackbar和closeSnackbar保存到全局类(示例答案中为SnackbarUtils.js)。

    看起来Snackbar.js不是一个组件。您只能在组件内部使用挂钩。我明白了,在SnackBar.js中调用SnackBar函数的最佳方式是什么?我应该从我调用它的任何组件中传入enqueueSnackbar和closeSnackbar函数吗?谢谢您提供的信息。我不知道这些事情。对不起,没问题!现在您已经看到了我的原始注释,我已将其删除。TypeError:无法对“Object(…)(…)”的属性“enqueueSnackbar”进行分解,因为它未定义。这是因为行
    const{enqueueSnackbar,closeSnackbar}=useSnackbar()
    
    <button onClick={()=>{
               SnackbarUtils.success("Hello")
            }}>Show</button>