React:将 match.params 传递给 children

React: Passing match.params to children

我做了一个私有路由功能:

function PrivateRoute({ children, ...rest }) {
  return (
    <Route
      {...rest}
      render={({ location }) =>
        isLoggedIn || isAccountVerified ? (
          children
        ) : (
          <Redirect
            to={{
              pathname: '/',
              state: { from: location }
            }}
          />
        )
      }
    />
  );
}

现在我想要的是 Route 组件的任何 child 都可以访问其 :params 属性:

 <PrivateRoute 
  path="/confirmed/:token"
  isAccountVerified={isAccountVerified}>
   <Confirmation />
 </PrivateRoute> 

这样我就可以在这个组件中使用它了:

class Confirmation extends Component {
  constructor(props) {
    super(props);

    this.state = {
     ...somestate
    };
  }

  componentDidMount() {
    var { token } = this.props.token; // Use it here!!!!
    axios
      .get(`http://localhost:8016/users/confirmation/${token}`)
      .then(response => {
        console.log('response', response);
        if (response.status === 200) {
          this.setState({
            responseMessage: response.data.msg
          });
        }
        this.setState(() => ({ confirmedParam }));
      })
      .catch(
        function(error) {
          if (error.response.status === 404) {
            this.setState({
              responseMessage: error.response.data.msg,
              error: true
            });
            return;
          }
          if (error.response.status === 400) {
            this.setState({
              responseMessage: error.response.data.msg,
              error: true
            });
            return;
          }
        }.bind(this)
      );
  }

  render() {
   .....
  }

如何实现?黑魔法很好...

20 年 3 月 22 日更新

客户端路由:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { logInUser, logOutUser } from '../store/reducers/users/index';
import { bindActionCreators } from 'redux';

import { Switch, Route, Redirect, withRouter } from 'react-router-dom';

import LinkNavWithLayout from './LinkNavWithLayout';
import Index from './home';
import Profile from './profile';
import Dashboard from './dashboard';
import ForgotPassword from './forgotPassword';
import ResetPassword from './resetPassword';
import Login from './login';
import Confirmation from './confirmation';
import { modalStateOn, modalStateOff } from '../store/reducers/ui/index';

import Register from './register';

class App extends Component {
  static getInitialProps({
    store,
    isAccountVerified,
    isLoggedIn,
    logInUser,
    logOutUser
  }) {
    console.log('store', store);

    return { store, isAccountVerified, isLoggedIn, logInUser, logOutUser };
  }

  constructor(props) {
    super(props);
  }

  render() {
    const { isLoggedIn, isAccountVerified } = this.props;
    console.log('isAccountVerified ', isAccountVerified);

    console.log('this.props ', this.props);

    let navBars = [
      { name: 'Home', path: '/' },
      { name: 'Profile', path: '/profile' },
      { name: 'Dashboard', path: '/dashboard' },
      { name: 'Log in', path: '/login' },
      { name: 'Register', path: '/register' }
    ];

    // function PrivateRoute({ children, ...rest }) {
    //   return (
    //     <Route
    //       {...rest}
    //       render={({ location }) =>
    //         isLoggedIn || isAccountVerified ? (
    //           { ...children }
    //         ) : (
    //           <Redirect
    //             to={{
    //               pathname: '/',
    //               state: { from: location }
    //             }}
    //           />
    //         )
    //       }
    //     />
    //   );
    // }

    function PrivateRoute({ component: Component, ...rest }) {
      return (
        <Route
          {...rest}
          render={(
            { location, ...routeProps } // match.params.token is here
          ) =>
            isLoggedIn || isAccountVerified ? (
              <Component {...routeProps} />
            ) : (
              <Redirect
                to={{
                  pathname: '/',
                  state: { from: location }
                }}
              />
            )
          }
        />
      );
    }

    return (
      <>
        <Switch>
          <Route
            path="/"
            isLoggedIn={isLoggedIn}
            exact
            render={props => (
              <LinkNavWithLayout {...props} data={navBars}>
                <Index />
              </LinkNavWithLayout>
            )}
          />

          <PrivateRoute path="/profile" isLoggedIn={isLoggedIn}>
            <LinkNavWithLayout data={navBars}>
              <Profile user />
            </LinkNavWithLayout>
          </PrivateRoute>

          <PrivateRoute path="/dashboard" isLoggedIn={isLoggedIn}>
            <LinkNavWithLayout data={navBars}>
              <Dashboard />
            </LinkNavWithLayout>
          </PrivateRoute>

          <Route path="/login" render={props => <Login {...props} />} />

          <Route
            path="/forgot_password"
            render={props => <ForgotPassword {...props} />}
          />

          <Route path="/reset_password" render={props => <ResetPassword {...props} />} />

          <PrivateRoute
            path="/confirmed/:token"
            isAccountVerified={isAccountVerified}
            component={Confirmation}
          />

          <Route path="/register" render={props => <Register {...props} />} />

          <Route
            component={({ location }) => (
              <h1>
                Sorry but the page{' '}
                <p style={{ fontWeight: 'strong' }}>{location.pathname.substring(1)} </p>{' '}
                Page, Could Not be found
              </h1>
            )}
          />
        </Switch>
      </>
    );
  }
}

function mapStateToProps(state) {
  const { ui, users } = state;
  const { isLoggedIn, userAvatar, isAccountVerified } = users;
  const { modalActive } = ui;
  return { isLoggedIn, isAccountVerified, userAvatar, modalActive };
}
const mapDispatchToProps = dispatch =>
  bindActionCreators({ modalStateOn, modalStateOff, logInUser, logOutUser }, dispatch);

export default withRouter(
  connect(
    mapStateToProps,
    mapDispatchToProps
  )(App)
);

确认组件:

import React, { Component } from 'react';

import { Loader, Dimmer, Transition, Message } from 'semantic-ui-react';

import { hasBeenVerified } from '../../store/reducers/users/index';

import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

class Confirmation extends Component {
  constructor(props) {
    super(props);

    this.state = {
      duration: 500,
      confirmedParam: false,
      responseMessage: {},
      error: false
    };
  }

  componentDidMount() {
    var { token } = this.props.match.params;
    axios
      .get(`http://localhost:8016/users/confirmation/${token}`)
      .then(response => {
        console.log('response', response);
        if (response.status === 200) {
          hasBeenVerified();
          this.setState({
            responseMessage: response.data.msg
          });
        }

        this.setState(() => ({ confirmedParam }));
      })
      .catch(
        function(error) {
          if (error.response.status === 404) {
            this.setState({
              responseMessage: error.response.data.msg,
              error: true
            });
            return;
          }
          if (error.response.status === 400) {
            this.setState({
              responseMessage: error.response.data.msg,
              error: true
            });
            return;
          }
        }.bind(this)
      );
  }

  render() {
    var { responseMessage } = this.state;

    var { isAccountVerified } = this.props;
    return (
      <div className="login-form">
        <Transition
          visible={isAccountVerified}
          unmountOnHide={true}
          animation="scale"
          duration={duration}
        >
          {isAccountVerified ? (
            <Dimmer active inverted>
              <Loader />
            </Dimmer>
          ) : (
            <Message success header={responseMessage[0]} />
          )}
        </Transition>
      </div>
    );
  }
}

function mapStateToProps(state) {
  const { users } = state;
  const { isAccountVerified } = users;

  return { isAccountVerified };
}

const mapDispatchToProps = dispatch => bindActionCreators({ hasBeenVerified }, dispatch);

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

这是应该发生的事情,在注册后他们必须等待我通过 nodemailer 发送的电子邮件,一旦收到它就会有一个 link指向上面的路线,例如http://localhost:8016/confirmed/245dd2b35b634d11d2e10770a994c810

然后该组件对我的 express 应用程序生成 XHR

router.route('/confirmation/:token').get((req, res, next) => {
  var usersToken = req.params.token;
  try {
    Token.findOne({ token: usersToken }, function(err, token) {
      if (err)
        return res.status(404).send({
          msg: ['We were unable to find a valid token. Your token my have expired.']
        });
      // If we found a token, find a matching user
      User.findOne({ _id: token._userId, email: req.body.username }, function(err, user) {
        if (err)
          return res
            .status(404)
            .send({ msg: ['We were unable to find a user for this token.'] });
        if (user.isVerified)
          return res.status(400).send({
            msg: ['This user has already been verified.']
          });

        // Verify and save the user
        user.isVerified = true;
        user.save(function(err) {
          if (err) {
            return res.status(500).send({ msg: err.message });
          }
        });
        return res
          .status(200)
          .send({ msg: ['The account has been verified. Please log in.'] });
      });
    });
  } catch (err) {
    return next(err);
  }
});

并且结果应该更新给用户,即 Confirmation 组件....

传递组件引用并在您的路由中初始化它,而不是在父级别。

function PrivateRoute({ component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      render={({ location, ...routeProps }) => // match.params.token is here
        isLoggedIn || isAccountVerified ? (
          <Component {...routeProps} /> 
        ) : (
          <Redirect
            to={{
              pathname: '/',
              state: { from: location }
            }}
          />
        )
      }
    />
  );
}
<PrivateRoute 
  path="/confirmed/:token"
  isAccountVerified={isAccountVerified}
  component={Confirmation}
/>

spread operator 添加到 ...children 解决了我的问题!

function PrivateRoute({ children, ...rest }) {
  return (
    <Route
      {...rest}
      render={({ location }) =>
        isLoggedIn || isAccountVerified ? (
          { ...children } /* spread operator */
        ) : (
          <Redirect
            to={{
              pathname: '/',
              state: { from: location }
            }}
          />
        )
      }
    />
  );
}