简体   繁体   English

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

[英]Converting stateless React component having arguments to stateful

Inside my React JS project, I am working on the PrivateRoutes . 在我的React JS项目中,我正在研究PrivateRoutes I have gone through this example of private routing and authenticating using react-router-dom . 我已经完成了私有路由和使用react-router-dom身份验证的示例。

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

According to this documentation, they have created a PrivateRoute as a stateless component. 根据此文档,他们创建了一个PrivateRoute作为无状态组件。

But my requirement is to convert it to stateful React component as I want to connect my PrivateRoute component to redux store. 但我的要求是将其转换为有状态的React组件,因为我想将我的PrivateRoute组件连接到redux存储。

Here is my code. 这是我的代码。

stateless component 无国籍组成部分

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;

I converted this component to stateful React component like this. 我将此组件转换为有状态的React组件,就像这样。

stateful React component 有状态的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);

Here, I am reading the data from redux store to check whether the user is authenticated or not. 在这里,我正在读取redux store中的数据,以检查用户是否已经过身份验证。

But the way I am converting the stateless component to stateful isn't correct. 但是我将无状态组件转换为有状态的方式是不正确的。

Am I passing the arguments render({ component: Component, ...rest }) correctly? 我正确地传递了参数render({ component: Component, ...rest })吗?

Will connecting the PrivateRoute with redux store create any problem with props as state=>state will map state to props as well as ...rest will have props object? PrivateRoute与redux商店连接会产生任何props问题,因为state=>state会将state映射到props以及...rest会有props对象吗?

Not sure what is happening inside the code. 不确定代码中发生了什么。

Update AppRouter.js 更新 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>
    );
}

In general, converting a stateless functional component (SFC) to a Component is done like this: 通常,将无状态功能组件(SFC)转换为Component方式如下:

  1. Create the class shell for it. 为它创建class shell。

  2. Copy the SFC's body to the render method. 将SFC的主体复制到render方法。 If the SFC was an arrow function, add a return as necessary to render . 如果SFC是箭头函数,则根据需要添加return以进行render

  3. Change any references to props in the render method to this.props (or just add const { props } = this; at the top). render方法中对props任何引用更改为this.props (或者只添加const { props } = this;在顶部)。 SFCs receive their props in their arguments, but a component receives them as arguments to its constructor; SFC在其参数中接收它们的道具,但是一个组件将它们作为其构造函数的参数接收; the default constructor will save them as this.props . 默认构造函数将它们保存为this.props

    In your case, it's using destructuring on its arguments, so you could do the same with this.props on the right-hand side of the destructuring: 在你的情况下,它在其参数上使用解构,所以你可以在this.props的右边使用this.props做同样的this.props

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

That's it. 而已。 In your code, you've added parameters to the render function, but it doesn't get called with any arguments, and you've only changed props to this.props a bit haphazardly (including changing auth.isAuthenticated to this.props.customer.isAuthenticated for some reason). 在你的代码中,你已经为render函数添加了参数,但它不会被任何参数调用,并且你只是偶然地将props更改为this.props (包括将auth.isAuthenticated更改为this.props.customer.isAuthenticated由于某种原因)。

So applying 1-3 above: 所以应用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"/>
        )
      }
    />;
  }
}

Your stateful component should be: 您的有状态组件应该是:

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

Please see that there is some issue in render parameter of Route . 请注意Route render参数中存在一些问题。 Here you have props as function param but still using this.props.customer , don't know the use case hence please fix it as per your application. 这里你有props作为函数参数,但仍然使用this.props.customer ,不知道用例因此请根据您的应用程序修复它。

Apart from it Component and all the other data is already there in props of the component. 除了它之外, Component和所有其他数据已经存在于组件的道具中。 It won't be available in parameter of render method in component. 它在组件中的render方法的参数中不可用。 Same destructuring as available in stateless component can be written in render method as shown in code above. 无状态组件中可用的相同解构可以用render方法编写,如上面的代码所示。

Will connecting the PrivateRoute with redux store create any problem with props? 将PrivateRoute与redux商店连接会产生道具问题吗?

Yes, it would. 是的,它会的。 The way you have connected to the store will make store data available in props of component but external props passed to component will not be available. 您连接到商店的方式将使商店数据在组件的props中可用,但传递给组件的外部props将不可用。

For that you have to handle it in mapStateToProps function: 为此你必须在mapStateToProps函数中处理它:

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

Here mapStateToProps has second parameter which has the external own props passed to component. 这里mapStateToProps有第二个参数,它有外部自己的道具传递给组件。 So you have to return it as well to make it available in component props . 所以你必须返回它以使其在组件props可用。

Now connect would be like: 现在连接就像:

export default connect(mapStateToProps)(PrivateRoute);

I was having two queries. 我有两个疑问。

1) How to convert to Stateful Functional Component? 1)如何转换为有状态功能组件? 2) After connecting to the redux store will the props create a problem? 2)连接到redux商店后道具会产生问题吗?

My first query was solved by the answer provided by TJCrowder . 我的第一个查询是由TJCrowder提供的答案解决的。

For a second query, I tried connecting the redux store to the PrivateRoute and I did get the data I was looking for. 对于第二个查询,我尝试将redux store连接到PrivateRoute ,我确实获得了我正在寻找的数据。

Here is the code which worked for me. 这是适合我的代码。

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

Using this code I got the data that is coming from the routes, as well as the redux state inside the props. 使用此代码,我获得了来自路由的数据,以及props内的redux状态。

This is getting data coming from the routes const { component: Component, ...rest } = this.props; 这是从路由const { component: Component, ...rest } = this.props;获取数据const { component: Component, ...rest } = this.props;

This is the data coming from the redux store. 这是来自redux商店的数据。 const {customer} = this.props;

@TJCrowder has already written how to convert stateless component to stateful component in those 3 steps. @TJCrowder已经编写了如何在这3个步骤中将无状态组件转换为有状态组件。 so i will just write about connecting component to redux store like you did. 所以我会像你一样写关于将组件连接到redux商店。

I think connected components should always define mapStateToProps and explicitly declare which data they depend on from the state. 我认为连接组件应始终定义mapStateToProps并显式声明它们从状态依赖的数据。

because the connected component rerenders if the connected property changes. 因为连接的组件会在连接的属性发生更改时重新呈现。 so it would be a bad idea to connect the whole application state to a component. 所以将整个应用程序状态连接到组件是个坏主意。 as it would mean that wheneever anything changes in application state rerender all connected components. 因为这意味着当应用程序状态发生任何变化时,重新渲染所有连接的组件。

better we define explicitly like the following that we depend on a property called data (or anything you have) from the state. 我们更好地明确定义,如下所示,我们依赖于一个名为data (或你拥有的任何东西)的属性。 so in this case this component will only rerender if state.data changes it wont rerender if state.xyz changes. 所以在这种情况下,该组件将仅在重新呈现state.data改变,如果它不会重新呈现state.xyz变化。

and this way you can take state.data and name it as you wish so it would not conflict with any existing props of the component. 这样你可以使用state.data并根据需要命名它,这样就不会与组件的任何现有道具发生冲突。

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

export default connect(mapStateToProps)(PrivateRoute);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM