【问题标题】:Rewriting React router v4 class based code to v6 functional based将基于 React 路由器 v4 类的代码重写为基于 v6 功能的代码
【发布时间】: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} alt="Google" /> Log in with Google</a>
                <a className="btn btn-block social-btn facebook" href={FACEBOOK_AUTH_URL}>
                    <img src={fbLogo} alt="Facebook" /> Log in with Facebook</a>
                <a className="btn btn-block social-btn github" href={GITHUB_AUTH_URL}>
                    <img src={githubLogo} alt="Github" /> 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;


我想弄清楚什么

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

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

【问题讨论】:

    标签: javascript reactjs react-hooks react-router react-router-dom


    【解决方案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

    你有几个选择:

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

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

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

        const navigate = useNavigate();
        
        ...
        
        navigate("....", { state: {}, replace: true });
        
      • useLocation

        const { pathname, state } = useLocation();
        
    2. 创建一个自定义的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
    猜你喜欢
    • 2023-04-11
    • 2022-06-20
    • 2020-02-20
    • 2021-03-02
    • 1970-01-01
    • 1970-01-01
    • 2018-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多