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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了将具有参数的无状态React组件转换为有状态相关的知识,希望对你有一定的参考价值。

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

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

根据这个文档,他们创建了一个PrivateRoute作为无状态组件。

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

这是我的代码。

无国籍组成部分

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 store中的数据,以检查用户是否已经过身份验证。

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

我正确地传递了render({ component: Component, ...rest })的论点吗?

连接PrivateRoute与redux商店会产生props的任何问题,因为state=>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>
    );
}
答案

通常,将无状态功能组件(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"/>
        )
      }
    />;
  }
}
另一答案

您的有状态组件应该是:

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

请注意renderRoute参数中存在一些问题。在这里你有props作为功能参数但仍然使用this.props.customer,不知道用例因此请根据您的应用程序修复它。

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

将PrivateRoute与redux商店连接会产生道具问题吗?

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

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

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

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

现在连接就像:

export default connect(mapStateToProps)(PrivateRoute);
另一答案

我有两个疑问。

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

使用此代码,我获得了来自路由的数据,以及props内的redux状态。

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

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

另一答案

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

我认为连通组件应始终定义mapStateToProps并明确声明它们依赖于哪种数据。

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

更好的是我们明确定义如下,我们依赖于一个名为data(或你拥有的任何东西)的属性。所以在这种情况下,如果state.data发生变化,那么这个组件只会重新渲染,如果state.xyz发生变化则不会重新渲染。

这样你可以使用state.data并根据需要命名它,这样就不会与组件的任何现有道具发生冲突。

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

export default connect(mapStateToProps)(PrivateRoute);

以上是关于将具有参数的无状态React组件转换为有状态的主要内容,如果未能解决你的问题,请参考以下文章

React useState() 使用指南

Blazor中的无状态组件

反应:从类转换为具有状态的功能组件

有状态和功能性无状态组件

React拓展 - setState - 路由组件懒加载 - Hooks - Fragment - Context - PureComponent - 插槽 - 错误边界 - 组件通信方式总结(代码片

如何在 React Native 的无状态功能组件中扩展原生组件的 props TypeScript 接口?