将基于 React 路由器 v4 类的代码重写为基于 v6 功能的代码

Posted

技术标签:

【中文标题】将基于 React 路由器 v4 类的代码重写为基于 v6 功能的代码【英文标题】:Rewriting React router v4 class based code to v6 functional based 【发布时间】:2022-01-20 08:49:05 【问题描述】:

我正在尝试使用 react 和 spring boot 实现 oauh 登录,我找到了一个可以学习的教程。

我遇到的问题是它使用 React Router v4,我想更新它以使用 React Router v6 并改用功能组件。

Login.js

import React,  Component  from 'react';
import './Login.css';
import  GOOGLE_AUTH_URL, FACEBOOK_AUTH_URL, GITHUB_AUTH_URL, ACCESS_TOKEN  from '../../constants';
import  login  from '../../util/APIUtils';
import  Link, Redirect  from 'react-router-dom'
import fbLogo from '../../img/fb-logo.png';
import googleLogo from '../../img/google-logo.png';
import githubLogo from '../../img/github-logo.png';
import Alert from 'react-s-alert';


class Login extends Component 
    componentDidMount() 
        // If the OAuth2 login encounters an error, the user is redirected to the /login page with an error.
        // Here we display the error and then remove the error query parameter from the location.
        if(this.props.location.state && this.props.location.state.error) 
            setTimeout(() => 
                Alert.error(this.props.location.state.error, 
                    timeout: 5000
                );
                this.props.history.replace(
                    pathname: this.props.location.pathname,
                    state: 
                );
            , 100);
        
    
    
    render() 
        if(this.props.authenticated) 
            return <Redirect
                to=
                pathname: "/",
                state:  from: this.props.location 
            />;            
        

        return (
            <div className="login-container">
                <div className="login-content">
                    <h1 className="login-title">Login to SpringSocial</h1>
                    <SocialLogin />
                    <div className="or-separator">
                        <span className="or-text">OR</span>
                    </div>
                    <LoginForm ...this.props />
                    <span className="signup-link">New user? <Link to="/signup">Sign up!</Link></span>
                </div>
            </div>
        );
    


class SocialLogin extends Component 
    render() 
        return (
            <div className="social-login">
                <a className="btn btn-block social-btn google" href=GOOGLE_AUTH_URL>
                    <img src=googleLogo  /> Log in with Google</a>
                <a className="btn btn-block social-btn facebook" href=FACEBOOK_AUTH_URL>
                    <img src=fbLogo  /> Log in with Facebook</a>
                <a className="btn btn-block social-btn github" href=GITHUB_AUTH_URL>
                    <img src=githubLogo  /> Log in with Github</a>
            </div>
        );
    


App.js

这是带有路由的 App.js,我已将其更新为使用功能组件和 React Router v6。
//imports left out

function App() 

  const [globalUserState, setGlobalUserState] = useState(
    authenticated: false,
    currentUser: null,
    loading: true
  );

  useEffect(() => 
    loadCurrentlyLoggedInUser();
  )

  const loadCurrentlyLoggedInUser = () => 
    getCurrentUser()
      .then(res => 
        setGlobalUserState(
          currentUser: res,
          authenticated: true,
          loading: false
        );
      ).catch(err => 
        setGlobalUserState(
          loading: false
        )
      )
  

  const handleLogout = () => 
    localStorage.removeItem(ACCESS_TOKEN);
    setGlobalUserState(
      authenticated: false,
      currentUser: null
    );
    Alert.success("You're safely logged out!");
  


  return (
    <Router>
      <div className="app">

        <div className="app-header">
          <AppHeader />
        </div>
        <Routes>
          <Route path="/" element=<Home /> />
          <Route path="/profile" element=<SecuredRoute> <Profile /> </SecuredRoute> />
          <Route path="/login" element=(props) => <Login authenticated=globalUserState.authenticated ...props /> />
          <Route path="/signup" element=(props) => <Signup authenticated=globalUserState.authenticated ...props /> />
          <Route path="/oauth2/redirect" element=<OAuth2RedirectHandler /> />
          <Route path="*" element=<Notfound /> />
        </Routes>
 
        <Alert stack=limit: 3 
          timeout = 3000
          position='top-right' effect='slide' offset=65 
        />
       </div>
  </Router>
  );


export default App;


我想弄清楚什么

    我正在努力理解与 v6(location.state.error、history.replace、location.pathname 等)和功能组件而不是基于类的反应路由器功能的等价物。

    另外,如果有人可以解释这一行,请 &lt;LoginForm ...this.props /&gt;

【问题讨论】:

【参考方案1】:

第一季度

我很难理解 react 路由器的等价物 v6 的功能(location.state.error,history.replace, location.pathname 等)和功能组件而不是类 基于。

react-router-dom v6 中不再有路由道具,即没有historylocation,也没有matchRoute 组件也不再具有引用 React 组件或返回 JSX 的函数的 componentrender 属性,取而代之的是采用 JSX 文字的 element 属性,即 ReactElement .

如果我正确理解您的问题,您是在问如何使用 RRDv6 with 类组件 LoginSignup

你有几个选择:

    也将 LoginSignup 转换为 React 函数组件并使用新的 React 钩子。

    我不会介绍转换,但要使用的钩子是:

    useNavigate - history 对象被 navigate 函数替换。

    const navigate = useNavigate();
    
    ...
    
    navigate("....",  state: , replace: true );
    

    useLocation

    const  pathname, state  = useLocation();
    

    创建一个自定义的withRouter 组件,该组件可以使用钩子并将它们作为道具传递。

    const withRouter = WrappedComponent => props => 
      const navigate = useNavigate();
      const location = useLocation();
      // etc... other react-router-dom v6 hooks
    
      return (
        <WrappedComponent
          ...props
          navigate=navigate
          location=location
          // etc...
        />
      );
    ;
    

    装饰LoginSignup 出口:

    export default withRouter(Login);
    

    this.props.history.push 交换到this.props.navigate

    componentDidMount() 
      // If the OAuth2 login encounters an error, the user is redirected to the /login page with an error.
      // Here we display the error and then remove the error query parameter from the location.
      if (this.props.location.state && this.props.location.state.error) 
        setTimeout(() => 
          const  pathname, state  = this.props.location;
          Alert.error(state.error,  timeout: 5000 );
          this.props.navigate(
            pathname,
             state: , replace: true 
          );
        , 100);
      
    
    

剩下的就是修复 App 中的路由,以便它们正确呈现 JSX。

<Router>
  <div className="app">
    <div className="app-header">
      <AppHeader />
    </div>
    <Routes>
      <Route path="/" element=<Home /> />
      <Route
        path="/profile"
        element=(
          <SecuredRoute>
            <Profile />
          </SecuredRoute>
        )
      />
      <Route
        path="/login"
        element=<Login authenticated=globalUserState.authenticated />
      />
      <Route
        path="/signup"
        element=<Signup authenticated=globalUserState.authenticated />
      />
      <Route path="/oauth2/redirect" element=<OAuth2RedirectHandler /> />
      <Route path="*" element=<Notfound /> />
    </Routes>

    <Alert stack=limit: 3 
      timeout = 3000
      position='top-right' effect='slide' offset=65 
    />
  </div>
</Router>

第二季度

另外,如果有人可以解释这一行,请&lt;LoginForm ...this.props /&gt;

这只是简单地将所有传递给父组件的道具复制/传递给LoginForm 组件。

<LoginForm ...this.props />

Login 传递了一个 authenticated 道具以及注入的任何新“路由道具”,以及您可能正在使用的任何其他 HOC 注入的任何其他道具,以上将它们全部传递给 LoginForm .

【讨论】:

非常感谢您的明确解释。感谢您的帮助:D

以上是关于将基于 React 路由器 v4 类的代码重写为基于 v6 功能的代码的主要内容,如果未能解决你的问题,请参考以下文章

在 react-router v4 中将自定义道具传递给路由器组件

React Hooks with React Router v4 - 我如何重定向到另一个路由?

路由不适用于 React 路由器 v4

使用 React 路由器 v4 在 react-boilerplate 中加载异步减速器和 sagas

React Router v4 - 具有更改的默认路由的动态配置

如何使用 react-router v4 检测路由变化?