首页 > 解决方案 > 属性未定义 - 在 React 中从类组件切换到函数挂钩组件

问题描述

我在尝试将我的训练营项目代码从类组件切换到有状态功能组件时遇到了这个错误:

TypeError: Cannot read property 'user' of undefined
(anonymous function)
C:/Proiect curs react/src/pages/Login/Login.jsx:35
  32 | // }
  33 | 
  34 | useEffect((prevProps) => {
> 35 |  if (props.user !== prevProps.user) {
     | ^  36 |      props.history.push("/");
  37 |  }
  38 | }, [props.user, props.history]);

这发生在 Login 组件中,该组件负责显示通过 Google 连接的选项 - 使用 firebase 服务,并重定向到主页,这是 useEffect 试图做的。

下面在代码中注释的是 componentDidUpdate 方法的代码,它在组件是一个类时执行此操作。

import React, { useEffect } from "react";
import { Link } from "react-router-dom";
import Logo from "../../assets/images/logo.png";
import { ReactComponent as Google } from "../../assets/icons/google.svg";
import "./Login.css";
import { connect } from "react-redux";
import { loginUser } from "../../redux/actions/user";

const Login = (props) => {
    // componentDidUpdate(prevProps) {
    //     if (this.props.user !== prevProps.user) {
    //         this.props.history.push('/');
    //     }
    // }

    useEffect((prevProps) => {
        if (props.user !== prevProps.user) {
            props.history.push("/");
        }
    }, [props.user, props.history]);

    return (
        <div className="login-page">
            <Link to="/">
                <img src={Logo} alt="logo" className="mb-5" />
            </Link>
            <h1 className="h2">Login</h1>
            <p>Alege providerul cu care vrei să vrei să te loghezi:</p>
            <button
                className="btn btn-outline-dark d-flex align-items-center"
                onClick={() => props.signInWithGoogle()}
            >
                <Google className="w-50 mr-3" />
                <span className="text-nowrap">Loghează-te cu Google</span>
            </button>
        </div>
    );
};

function mapStateToProps(state) {
    return {
        user: state.user.data,
    };
}

function mapDispatchToProps(dispatch) {
    return {
        signInWithGoogle: () => dispatch(loginUser()),
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(Login);

此外,下面是包含整个应用程序的 github 存储库的链接。

https://github.com/vradu007/radu-shop

如果你能告诉我我做错了什么,请告诉我——我没有在训练营学习过钩子,但我在工作中使用它们,我一直在尝试通过在线查找来掌握它。谢谢!

编辑:使用该答案,我收到以下错误:

./src/pages/Login/Login.jsx
  Line 10:17:  React Hook "useRef" is called in function "getPrevious" which is neither a React function component or a custom React Hook fun
ction     react-hooks/rules-of-hooks
  Line 11:5:   React Hook "useEffect" is called in function "getPrevious" which is neither a React function component or a custom React Hook 
function  react-hooks/rules-of-hooks

无论我是否导入 useRef,都会重现该错误。当前组件如下所示:

import React, { useEffect, useRef } from "react";
import { Link } from "react-router-dom";
import Logo from "../../assets/images/logo.png";
import { ReactComponent as Google } from "../../assets/icons/google.svg";
import "./Login.css";
import { connect } from "react-redux";
import { loginUser } from "../../redux/actions/user";

function getPrevious(value) {
    const ref = useRef();
    useEffect(() => {
        ref.current = value;
    });
    return ref.current;
}

const Login = (props) => {
    // componentDidUpdate(prevProps) {
    //     if (this.props.user !== prevProps.user) {
    //         this.props.history.push('/');
    //     }
    // }
    
    const { user, history } = props;
    const previous = getPrevious({ user, history });
    useEffect(
        (prevProps) => {
            if (props.user !== previous.user) {
                props.history.push("/");
            }
        },
        [props.user, props.history]
    );

    return (
        <div className="login-page">
            <Link to="/">
                <img src={Logo} alt="logo" className="mb-5" />
            </Link>
            <h1 className="h2">Login</h1>
            <p>Alege providerul cu care vrei să vrei să te loghezi:</p>
            <button
                className="btn btn-outline-dark d-flex align-items-center"
                onClick={() => props.signInWithGoogle()}
            >
                <Google className="w-50 mr-3" />
                <span className="text-nowrap">Loghează-te cu Google</span>
            </button>
        </div>
    );
};

function mapStateToProps(state) {
    return {
        user: state.user.data,
    };
}

function mapDispatchToProps(dispatch) {
    return {
        signInWithGoogle: () => dispatch(loginUser()),
    };
}

export default connect(mapStateToProps, mapDispatchToProps)(Login);

标签: reactjsfirebasereact-hooks

解决方案


由于您无法使用 useEffect 达到 prevProps ,因此另一种方法是使用 useRef 保存该值然后进行比较。

import React, { useEffect , useRef} from "react";

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

并为您的 useEffect 进行此更改

const previous = usePrevious({props.user, props.history});
useEffect((prevProps) => {
    if (props.user !== previous.user) {
        props.history.push("/");
    }
}, [props.user, props.history]);

我的答案基于该答案:如何在 React Hooks useEffect 上比较 oldValues 和 newValues?


推荐阅读