【问题标题】:Converting stateless React component having arguments to stateful将具有参数的无状态 React 组件转换为有状态
【发布时间】:2018-12-02 14:05:58
【问题描述】:

在我的 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 存储连接会产生props 的任何问题,因为state=&gt;statestate 映射到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>
    );
}

【问题讨论】:

  • 如果您将所有必需的数据传递给props,那么尽管没有state,为什么还要将其转换为有状态组件?
  • 我想把这个组件连接到redux store,我确实连接了它,但是this.props.customer.isAuthenticated对我来说是不可用的,所以我想我应该把它转换成一个有状态的组件。

标签: javascript reactjs react-router react-redux


【解决方案1】:

一般来说,将无状态功能组件 (SFC) 转换为 Component 是这样完成的:

  1. 为其创建class shell。

  2. 将 SFC 的主体复制到 render 方法。如果 SFC 是箭头函数,请根据需要将 return 添加到 render

  3. render 方法中对props 的所有引用更改为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"/>
        )
      }
    />;
  }
}

【讨论】:

  • 请也解决以下部分:“将PrivateRoute 与redux 存储连接会产生props 的任何问题,因为state=&gt;state 将映射stateprops 以及@987654344 @ 会有 props 对象吗?”。如果组件从它使用的地方得到一个 prop user 并且还有一个状态字段 user ,我将在渲染函数中获得哪个用户值?
  • @InusSaha - 我不知道,我不使用 Redux。
  • @InusSaha @Vishal Shetty 是的,在这种情况下,将 PrivateRoute 与 redux 存储连接会产生问题,因为传递给组件的外部道具在组件道具中不可用。您必须在mapStateToProps 函数中处理它
【解决方案2】:

你的有状态组件应该是:

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"/>
          )
        }
      />
    );
  }
}

请查看Routerender 参数有问题。这里你有 props 作为函数参数但仍然使用 this.props.customer,不知道用例因此请根据你的应用程序修复它。

除了 Component 和所有其他数据已经存在于组件的 props 中。它不会在组件中render 方法的参数中可用。可以在render 方法中编写与无状态组件中可用的相同的解构,如上面的代码所示。

将 PrivateRoute 与 redux store 连接会不会对 props 产生任何问题?

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

为此,您必须在 mapStateToProps 函数中处理它:

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

这里mapStateToProps 有第二个参数,它有外部自己的道具传递给组件。因此,您还必须将其返回以使其在组件 props 中可用。

现在连接会是这样的:

export default connect(mapStateToProps)(PrivateRoute);

【讨论】:

    【解决方案3】:

    我有两个问题。

    1) 如何转换为有状态功能组件? 2)连接redux store后props会不会出问题?

    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);
    

    使用这段代码,我得到了来自路由的数据,以及 props 中的 redux 状态。

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

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

    【讨论】:

    • 如果 state 和 props 属性不同,这很好。但考虑一下,如果你在 state 和 props 中都有相同的 name 属性,那么可能会有问题。您可能会失去一项财产。
    • 是的,你是对的,也需要找出解决这个问题的方法。
    • @VishalShetty 我认为使用此代码的道具中不会提供路线数据,或者您有路线数据存储?
    • @HridayModi 抱歉,我的意思是传递给路由的道具将在道具内部可用,并且 redux 存储数据将可用。正如Inus Saha 所说,如果来自路由的道具和 redux 存储具有相同的属性名称,那么我们将失去任何一个属性。
    • @HridayModi connect 将完整的 redux 存储/状态传递给组件,而不仅仅是任何子状态。因此,最好从状态中明确选择所需的数据。阅读我的回答。
    【解决方案4】:

    @T.J.Crowder 已经写了如何在这 3 个步骤中将无状态组件转换为有状态组件。所以我会像你一样写关于将组件连接到 redux 商店。

    我认为连接的组件应该始终定义mapStateToProps,并从状态中明确声明它们所依赖的数据。

    因为如果连接的属性发生变化,连接的组件会重新渲染。所以将整个应用程序状态连接到一个组件是一个坏主意。因为这意味着每当应用程序状态发生任何变化时,都会重新渲染所有连接的组件。

    我们更好地像下面这样明确地定义我们依赖于状态中名为data(或任何你拥有的)的属性。所以在这种情况下,这个组件只会在 state.data 发生变化时重新渲染,如果 state.xyz 发生变化,它不会重新渲染。

    这样你就可以获取 state.data 并根据需要命名它,这样它就不会与组件的任何现有 props 冲突。

    const mapStateToProps = (state, ownProps) => ({
        data: state.data
    });
    
    export default connect(mapStateToProps)(PrivateRoute);
    

    【讨论】:

    • 我认为一旦我们将组件连接到 redux 存储,即使更新的存储数据与组件无关,组件也会被渲染。
    • 不,不是那样的。您可以在此处阅读更多内容:stackoverflow.com/a/40386189/4541018 阅读第一段并阅读有关连接和组件重新渲染的更多信息。
    猜你喜欢
    • 2017-10-05
    • 2020-01-23
    • 2018-01-22
    • 1970-01-01
    • 2017-08-30
    • 2018-10-18
    • 2021-02-08
    • 2017-01-16
    • 2018-06-02
    相关资源
    最近更新 更多