React 组件安装两次

IT技术 reactjs redux react-router
2021-05-02 15:56:45

在我的 React/Redux/ReactRouterV4 应用程序的一小部分中,我有以下组件层次结构,

- Exhibit (Parent)
-- ExhibitOne
-- ExhibitTwo
-- ExhibitThree

在 Exhibit 的子项中,也可以渲染大约 6 种不同的可能路线。别着急,我会用一些代码来解释。

这是我的父展览组件

export class Exhibit extends Component {
  render() {
    const { match, backgroundImage } = this.props

    return (
      <div className="exhibit">
        <Header />
        <SecondaryHeader />

        <div className="journey"
          style={{
            color: 'white',
            backgroundImage: `url(${backgroundImage})`,
            backgroundSize: 'cover',
            backgroundRepeat: 'no-repeat',
            backgroundPosition: 'center-center'
          }}>

          <Switch>
            <Route path={`${match.url}/exhibit-one`} component={ExhibitOne} />
            <Route path={`${match.url}/exhibit-two`} component={ExhibitTwo} />
            <Route path={`${match.url}/exhibit-three`} component={ExhibitThree} />
            <Redirect to="/" />
          </Switch>
        </div>
      </div>
    )
  }
}

基本上,它的工作就是显示其中一个展品子组件,并设置背景图像。

这是子组件之一,ExhibitOne:

export default class ExhibitOne extends Component {
  constructor(props) {
    super(props)
  }

  render() {
    const { match } = this.props

    return (
      <div className="exhibit-one">
        <Switch>
          <Route path={`${match.url}/wall-one`} component={ExhibitHOC(WallOne)} />
          <Route path={`${match.url}/wall-two`} component={ExhibitHOC(WallTwo)} />
          <Route path={`${match.url}/wall-three`} component={ExhibitHOC(WallThree)} />
          <Route path={`${match.url}/wall-four`} component={ExhibitHOC(WallFour)} />
          <Route path={`${match.url}/wall-five`} component={ExhibitHOC(WallFive)} />
          <Route path={`${match.url}/wall-six`} component={ExhibitHOC(WallSix)} />
        </Switch>
      </div>
    )
  }
}

为了减少打字,我决定将组件包装在一个高阶组件中,其目的是调度一个动作,将在顶级 Exhibit 父组件上设置适当的背景图像。

这是高阶组件:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as actions from '../../actions/wall-background-image'

export default function(ComposedComponent) {
  class ExhibitHoc extends Component {

    componentDidMount = () => this.props.setBackgroundImage(`./img/exhibit-one/${this.getWall()}/bg.jpg`)

    getWall = () => {
      // this part isnt important. it is a function that determines what wall I am on, in order to set
      // the proper image.
    }

    render() {
      return <ComposedComponent />
    }
  }

  return connect(null, actions)(ExhibitHoc);
}

在 ExhibitOne 的初始加载时,通过查看控制台中的 Redux Logger,我可以看到 setBackgroundImage 操作创建者执行了两次。我最初倾向于使用 componentDidMount 是因为我认为使用它会限制动作创建者只执行一次。这是日志的屏幕截图:

在此处输入图片说明

我想我可能误解了高阶组件是如何工作的,或者它可能是某种 React Router V4 的东西?无论如何,对于为什么会执行两次,任何帮助将不胜感激。

3个回答

问题是component这里prop 是一个函数应用程序,它在每次渲染时产生一个新类。这将导致先前的组件卸载并安装新的组件(有关更多信息,请参阅react-router 的文档)。通常你会使用renderprop 来处理这个问题,但这不适用于高阶组件,因为在渲染期间使用 HOC 应用程序创建的任何组件无论如何都会在 React 的协调期间重新安装。

一个简单的解决方案是在ExhibitOne之外创建组件,例如:

const ExhibitWallOne = ExhibitHOC(WallOne);
const ExhibitWallTwo = ExhibitHOC(WallTwo);
..
export default class ExhibitOne extends Component {
  ..
          <Route path={`${match.url}/wall-one`} component={ExhibitWallOne} />
          <Route path={`${match.url}/wall-two`} component={ExhibitWallTwo} />
          ..
}

或者,根据包装器的作用,可以将其声明为呈现{this.props.children}而不是 parameter的普通组件<ComposedComponent/>,并将组件包装在 each 中Route

<Route path={`${match.url}/wall-one`}
       render={(props) => <Wrap><WallOne {...props}/></Wrap>}
/>

请注意,您需要使用render而不是component防止重新安装。如果组件不使用路由props,您甚至可以删除{...props}.

在 2020 年,这是由<React.StrictMode>包含<App />在新版本 Create React App 中组件引起的index.js修复了我所有组件的双重安装问题中删除了有问题的组件。我不知道这是设计使然还是什么,但是对于所有内容都看到两次 console.logs() 很烦人且具有误导性。

如果您使用“隐藏材质 UI React”,则每次调用它时它都会挂载您的组件。例如,我写了下面的一个:

<Hidden mdDown implementation="css">
    <Container component="main" maxWidth="sm">
        {content}
    </Container>
</Hidden>
<Hidden smUp implementation="css">
    {content}
</Hidden>

它调用两个隐藏组件中的两个内容。我花了很多时间。