javascript - 获取 TypeError : Object(. ..) 不是在 React 中更改路由时的函数

标签 javascript reactjs react-router react-component

不确定为什么会出现以下错误。

Uncaught TypeError: Object(...) is not a function
  at Redirect.componentDidUpdate (Redirect.js:42)

我的 routeInterceptor 组件的渲染方法:

render() {
  const { forbidden, notFound } = this.state;
  const { authed } = this.props;
  // const { location } = this.props;
  // const { pathname } = location;

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

  // If authentication is complete, but the user is not logged in,
  // redirect to the login view.

  /*
    Problem starts here, if I move the forbidden logic above this
    Everything works, however the user is then always redirected to the
    forbidden page instead of login
  */
  if (authed === false) return <Redirect to="/login" />;

  // If user is logged in and accesses an unathenticated view,
  // redirect to the Products view.
  if (authed === true) return <Products />;

  // if (forbidden && pathname !== '/login') return <Forbidden />;
  if (forbidden) return <Forbidden />;
  if (notFound) return <NotFound />;

  return <Loading />;
}

Redirect 组件中代码中断的地方:

Redirect.prototype.componentDidUpdate = function componentDidUpdate(prevProps) {
  var prevTo = createLocation(prevProps.to); // <- This line.
  var nextTo = createLocation(this.props.to);

  if (locationsAreEqual(prevTo, nextTo)) {
    warning(false, 'You tried to redirect to the same route you\'re currently on: ' + ('"' + nextTo.pathname + nextTo.search + '"'));
    return;
  }

  this.perform();
};

下面是 createLocation 的实现,它是 history 包的一部分:

https://github.com/ReactTraining/history/blob/master/modules/LocationUtils.js

这是prevProps的日志:

enter image description here

知道这里可能出了什么问题吗?

这是 LocationUtils.js 的所有代码,它是历史的一部分,包含 createLocation 函数。

import resolvePathname from "resolve-pathname";
import valueEqual from "value-equal";
import { parsePath } from "./PathUtils";

export const createLocation = (path, state, key, currentLocation) => {
  let location;
  if (typeof path === "string") {
    // Two-arg form: push(path, state)
    location = parsePath(path);
    location.state = state;
  } else {
    // One-arg form: push(location)
    location = { ...path };

    if (location.pathname === undefined) location.pathname = "";

    if (location.search) {
      if (location.search.charAt(0) !== "?")
        location.search = "?" + location.search;
    } else {
      location.search = "";
    }

    if (location.hash) {
      if (location.hash.charAt(0) !== "#") location.hash = "#" + location.hash;
    } else {
      location.hash = "";
    }

    if (state !== undefined && location.state === undefined)
      location.state = state;
  }

  try {
    location.pathname = decodeURI(location.pathname);
  } catch (e) {
    if (e instanceof URIError) {
      throw new URIError(
        'Pathname "' +
          location.pathname +
          '" could not be decoded. ' +
          "This is likely caused by an invalid percent-encoding."
      );
    } else {
      throw e;
    }
  }

  if (key) location.key = key;

  if (currentLocation) {
    // Resolve incomplete/relative pathname relative to current location.
    if (!location.pathname) {
      location.pathname = currentLocation.pathname;
    } else if (location.pathname.charAt(0) !== "/") {
      location.pathname = resolvePathname(
        location.pathname,
        currentLocation.pathname
      );
    }
  } else {
    // When there is no prior location and pathname is empty, set it to /
    if (!location.pathname) {
      location.pathname = "/";
    }
  }

  return location;
};

export const locationsAreEqual = (a, b) =>
  a.pathname === b.pathname &&
  a.search === b.search &&
  a.hash === b.hash &&
  a.key === b.key &&
  valueEqual(a.state, b.state);

最佳答案

想通了,但很痛苦!

这是一个竞争条件,因为当 routeInterceptor 改变路线时,新路线重新生成的速度不够快。

在另一个模块中,routeManager 我们需要将 location 添加到 connect 中,以便 watch 它。

路由管理器

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

// React router
import { Route, Switch } from 'react-router-dom';
import { ConnectedRouter } from 'react-router-redux';

// Actions
import { onAuthStateChange } from 'actions/Auth';

// MUI Components
import CssBaseline from '@material-ui/core/CssBaseline';

// Components
import Main from 'components/Common/main';
import Loading from 'components/Common/loading';
import * as components from 'components';

// Utils
import history from 'clientHistory';
import { cleanMapStateToProps } from 'utils/redux';
import { getRoutesArray } from 'utils/routes';

// Copy
import { PAGE_AUTH_REQS } from 'copy/Global/routes';

const {
  AUTHORIZED,
  UNAUTHORIZED
} = PAGE_AUTH_REQS;

const getComponentForRoute = compName => (
  components[compName] || (() => null)
);

const getRouteComponents = FILTER => getRoutesArray(FILTER)
  .map(route => ({
    ...route,
    component: getComponentForRoute(route.component)
  }))
  .map(route => (<Route {...route} key={route.label} />));

class RouteManager extends React.Component {
  componentDidMount() {
    this.props.onAuthStateChange();
  }

  renderAuthorizedRoutes() {
    const routes = getRouteComponents(AUTHORIZED);

    return (
      <ConnectedRouter history={history}>
        <Main
          signOut={this.signOut}
          notifications={this.props.notifications}
          currentNotification={this.props.currentNotification}
        >
          <CssBaseline />
          <Switch>
            {routes}
          </Switch>
        </Main>
      </ConnectedRouter>
    );
  }

  renderUnauthorizedRoutes() {
    const routes = getRouteComponents(UNAUTHORIZED);

    return (
      <ConnectedRouter history={history}>
        <Main
          signOut={this.signOut}
          notifications={this.props.notifications}
          currentNotification={this.props.currentNotification}
        >
          <CssBaseline />
          <Switch>
            {routes}
          </Switch>
        </Main>
      </ConnectedRouter>
    );
  }

  render() {
    const { authed } = this.props;

    if (authed === null) {
      return <Loading />;
    }

    return authed
      ? this.renderAuthorizedRoutes()
      : this.renderUnauthorizedRoutes();
  }
}

export const RouteManagerJest = RouteManager;

const mapDispatchToProps = dispatch => ({
  onAuthStateChange: () => { dispatch(onAuthStateChange()); }
});

export default connect(cleanMapStateToProps([
  'authed',
  'location', // <-- Needed to just add location here.
  'currentNotification',
  'notifications'
]), mapDispatchToProps)(RouteManager);

export default connect(cleanMapStateToProps([
  'authed',
  'location', // <-- Needed to just add location here.
  'currentNotification',
  'notifications'
]), mapDispatchToProps)(RouteManager);

routes.js

import { PAGE_AUTH_REQS } from 'copy/Global/routes';

export const PROP_CONTENT_ROUTE = '[[ContentRoute]]';

const exact = true;

const {
  ANY,
  AUTHORIZED,
  UNAUTHORIZED
} = PAGE_AUTH_REQS;

/**
 * Do not export routes - if you need to get a list of routes (as an array or
 * object), use one of the convenience methods down below.
 */
const routesConfig = {
  // Auth Routes => /:context
  authLogin: {
    label: 'Login',
    path: '/login',
    title: 'Login',
    authReq: UNAUTHORIZED,
    component: 'Login',
    exact
  },
  authResetPassword: {
    label: 'Reset',
    path: '/reset-password',
    title: 'Reset Password',
    authReq: UNAUTHORIZED,
    component: 'ResetPassword',
    exact
  },
  authRedirector: {
    label: 'Redirect',
    path: '/redirect',
    title: 'Firebase Redirect',
    authReq: UNAUTHORIZED,
    component: 'FirebaseRedirector',
    exact
  },
  authChangePassword: {
    label: 'Change',
    path: '/change-password/:oobCode/',
    title: 'Change Password',
    authReq: UNAUTHORIZED,
    component: 'ChangePassword',
    exact
  },
  authVerification: {
    label: 'Verify',
    path: '/verification',
    title: 'Verify your email',
    authReq: UNAUTHORIZED,
    component: 'Login',
    exact
  },
  authRestricted: {
    label: 'Restricted Access',
    path: '/restricted-access',
    title: 'Restricted Access',
    authReq: UNAUTHORIZED,
    component: 'RestrictedAccess',
    exact
  },

  products: {
    label: 'Products',
    path: '/(products)?',
    title: 'Products',
    authReq: AUTHORIZED,
    component: 'Products'
  },

  // ********************************************************
  // ********************************************************
  // ********************************************************
  // ANY ROUTES BELOW RouteInterceptor WILL NOT BE CONSIDERED
  // ********************************************************
  // ********************************************************
  // ********************************************************

  routeInterceptor: {
    label: null,
    title: null,
    authReq: ANY,
    component: 'RouteInterceptor'
  }
};

export default routesConfig;

现在它终于按预期工作了。

关于javascript - 获取 TypeError : Object(. ..) 不是在 React 中更改路由时的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51085536/

相关文章:

javascript - React-redux connect HOC 作为类装饰器,@connect

javascript - 无法显示通过 ajax 调用获得的数据

css - Tailwind 的 theme() 无法识别

javascript - React enzyme 无法测试 onclick 功能

javascript - 如何将多个 html 文件加载到 Div 中

javascript - 我如何知道用户或单个 IP 地址在我网站的页面上花费了多长时间?

javascript - 无法在 React.js 中导入 d3-queue?尝试导入错误 : 'queue' is not exported from 'd3' (imported as 'd3' )

javascript - 基于 React Router 过滤映射对象

reactjs - “react-router”不包含名为 'BrowserRouter' 的导出

javascript - React/Express 没有正确路由链接