将具有参数的无状态React组件转换为有状态

时间:2018-06-23 07:38:43

标签: javascript reactjs react-router react-redux

在我的React JS项目中,我正在研究PrivateRoutes。 我已经看过这个使用react-router-dom进行私有路由和身份验证的示例。

https://reacttraining.com/react-router/web/example/auth-workflow

根据本文档,他们已经创建了PrivateRoute作为无状态组件。

但是我需要将其转换为有状态的React组件,因为我想将PrivateRoute组件连接到redux存储。

这是我的代码。

无状态组件

import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {auth} from './Authentication';

const PrivateRoute = ({ component: Component, ...rest }) => (
    <Route
      {...rest}
      render={props =>
        auth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Component {...props} action="login"/>
        )
      }
    />
  );

  export default PrivateRoute;

我将此组件转换为有状态的React组件。

有状态的React组件

import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {auth} from './Authentication';
import {connect} from 'react-redux';

  class PrivateRoute extends React.Component {
    render({ component: Component, ...rest }) {
      return (
        <Route
          {...rest}
          render={props =>
            this.props.customer.isAuthenticated ? (
              <Component {...props} />
            ) : (
              <Component {...props} action="login"/>
            )
          }
        />
      );
    }
  }
  export default connect(state => state)(PrivateRoute);

在这里,我正在从redux存储中读取数据,以检查用户是否已通过身份验证。

但是我将无状态组件转换为有状态的方法是不正确的。

我可以正确传递参数render({ component: Component, ...rest })吗?

PrivateRoute与redux store连接会导致props出现任何问题,因为state=>state会将state映射到props以及...rest将有props个对象?

不确定代码中发生了什么。

更新 AppRouter.js

import React from 'react';
import {BrowserRouter, Route, Switch} from 'react-router-dom';
import {TransitionGroup, CSSTransition} from 'react-transition-group';
import PrivateRoute from './PrivateRoute';

import HomePage from './../components/HomePage';
import AboutUs from './../components/AboutUs';
import ContactUs from './../components/ContactUs';
import PageNotFound from './../components/PageNotFound';
import RestaurantList from '../components/RestaurantList';
import RestaurantMenu from '../components/RestaurantMenu';
import UserDetails from '../components/UserDetails';
import OrderConfirmation from '../components/OrderConfirmation';
import CustomerAccount from '../components/CustomerAccount';
import Logout from '../components/sections/Logout';


export default () => {
    return (
        <BrowserRouter>
            <Route render={({location}) => (
                <TransitionGroup>
                    <CSSTransition key={location.key} timeout={300} classNames="fade">
                        <Switch location={location}>
                            <Route path="/" component={HomePage} exact={true}/>
                            <Route path="/about" component={AboutUs} />
                            <Route path="/contact" component={ContactUs} />
                            <Route path="/restaurants" component={RestaurantList} />
                            <Route path="/select-menu" component={RestaurantMenu} />
                            <PrivateRoute path="/user-details" component={UserDetails} />
                            <PrivateRoute path="/order-confirmation" component={OrderConfirmation} />
                            <PrivateRoute path="/my-account" component={CustomerAccount} />
                            <PrivateRoute path="/logout" component={Logout} />

                            <Route component={PageNotFound} />
                        </Switch>
                    </CSSTransition>
                </TransitionGroup>
            )} />

        </BrowserRouter>
    );
}

4 个答案:

答案 0 :(得分:6)

通常,将无状态功能组件(SFC)转换为Component是这样的:

  1. 为其创建class shell。

  2. 将SFC的主体复制到render方法中。如果SFC是箭头功能,则根据需要向return添加render

  3. props方法中对render的所有引用更改为this.props(或仅在顶部添加const { props } = this;)。 SFC在其参数中接收其道具,但是组件将其作为其构造函数的参数接收;默认构造函数会将其另存为this.props

    在您的情况下,它在其参数上使用了解构,因此您可以对解构右侧的this.props进行同样的操作:

    const { component: Component, ...rest } = this.props;
    

就是这样。在您的代码中,您已经向render函数添加了参数,但是没有任何参数调用它,并且您只是随意地将props更改为this.props(包括出于某种原因将auth.isAuthenticated更改为this.props.customer.isAuthenticated

因此应用上述1-3:

// #1 - the shell
class PrivateRoute extends React.Component {
  // #2 - `render`, with the body of the SFC inside
  render() {
    // #3 - destructure `this.props`
    const { component: Component, ...rest } = this.props;
    // #2 (part 2) - add `return`
    return <Route
      {...rest}
      render={props =>
        auth.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Component {...props} action="login"/>
        )
      }
    />;
  }
}

答案 1 :(得分:1)

您的有状态组件应为:

class PrivateRoute extends React.Component {
  render() {
    const { component: Component, ...rest } = this.props;
    return (
      <Route
        {...rest}
        render={props =>
          this.props.customer.isAuthenticated ? (
            <Component {...props} />
          ) : (
            <Component {...props} action="login"/>
          )
        }
      />
    );
  }
}

请注意,render的{​​{1}}参数中存在一些问题。在这里,您将Route作为函数参数,但仍在使用props,不知道用例,因此请根据您的应用程序对其进行修复。

this.props.customer以外,组件的props中已经存在所有其他数据。在组件中的Component方法的参数中将不可用。如上代码所示,可以使用render方法编写与无状态组件中可用的相同的破坏操作。

将PrivateRoute与redux商店连接会导致道具出现问题吗?

是的,会的。您与商店的连接方式将使商店数据在组件的render中可用,但传递给组件的外部props将不可用。

为此,您必须在props函数中进行处理:

mapStateToProps

此处const mapStateToProps = (state, ownProps) => ({ ...state, ...ownProps }); 具有第二个参数,该参数具有传递给组件的外部自己的道具。因此,您还必须返回它以使其在组件mapStateToProps中可用。

现在连接将是:

props

答案 2 :(得分:0)

我有两个查询。

1)如何转换为有状态功能组件? 2)连接到Redux商店后,道具会出现问题吗?

我的第一个查询已由T.J.Crowder提供的答案解决。

对于第二个查询,我尝试将redux store连接到PrivateRoute,并且确实获得了我想要的数据。

这是对我有用的代码。

import React from 'react';
import {Route, Redirect} from 'react-router-dom';
import {connect} from 'react-redux';

class PrivateRoute extends React.Component {
  render() {
    const { component: Component, ...rest } = this.props;
    const {customer} = this.props;

    return <Route
      {...rest}
      render={props =>
        customer.isAuthenticated ? (
          <Component {...props} />
        ) : (
          <Component {...props} action="login"/>
        )
      }
    />;
  }
}

export default connect(state => state)(PrivateRoute);

使用此代码,我获得了来自路线的数据以及道具内部的redux状态。

这是从路线获取数据 const { component: Component, ...rest } = this.props;

这是来自redux存储的数据。 const {customer} = this.props;

答案 3 :(得分:0)

@ T.J。Crowder已经在这三个步骤中编写了如何将无状态组件转换为有状态组件。因此,我将像您一样撰写有关将组件连接到redux存储的文章。

我认为连接的组件应始终定义mapStateToProps并明确声明其依赖于状态的数据。

因为如果已连接的属性发生更改,则已连接的组件将重新渲染。因此将整个应用程序状态连接到组件将是一个坏主意。因为这意味着只要应用程序状态发生任何变化,都将重新呈现所有连接的组件。

最好像下面这样明确定义,我们依赖于状态中名为data(或您拥有的任何东西)的属性。因此,在这种情况下,仅当state.data更改时,该组件才会重新呈现;如果state.xyz更改,则不会重新呈现。

通过这种方式,您可以获取state.data并根据需要命名它,以免与该组件的任何现有属性冲突。

const mapStateToProps = (state, ownProps) => ({
    data: state.data
});

export default connect(mapStateToProps)(PrivateRoute);