React 组件安装两次 [英] React Component Mounting Twice

查看:50
本文介绍了React 组件安装两次的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

- 展览(父)-- 展览一-- 展品二-- 展品三

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

这是我的父展览组件:

export class Exhibit extends Component {使成为() {const { match, backgroundImage } = this.props返回 (<div className="展示"><标题/><SecondaryHeader/><div className="旅程"风格={{白颜色',backgroundImage: `url(${backgroundImage})`,背景尺寸:'封面',backgroundRepeat: '不重复',backgroundPosition: '中心-中心'}}><开关><路由路径={`${match.url}/exhibit-one`} component={ExhibitOne}/><路由路径={`${match.url}/exhibit-two`} component={ExhibitTwo}/><路由路径={`${match.url}/exhibit-three`} component={ExhibitThree}/><重定向到="/"/></开关>

)}}

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

这是子组件之一,ExhibitOne:

export default class ExhibitOne extends Component {构造函数(道具){超级(道具)}使成为() {const { 匹配 } = this.props返回 (<div className="exhibit-one"><开关><路由路径={`${match.url}/wall-one`} 组件={ExhibitHOC(WallOne)}/><路由路径={`${match.url}/wall-two`} component={ExhibitHOC(WallTwo)}/><路由路径={`${match.url}/wall-three`} component={ExhibitHOC(WallThree)}/><路由路径={`${match.url}/wall-four`} component={ExhibitHOC(WallFour)}/><路由路径={`${match.url}/wall-five`} component={ExhibitHOC(WallFive)}/><路由路径={`${match.url}/wall-six`} component={ExhibitHOC(WallSix)}/></开关>

)}}

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

这是高阶组件:

import React, { Component } from 'react';从'react-redux'导入{连接};从../../actions/wall-background-image"导入 * 作为动作导出默认函数(组合组件){类 ExhibitHoc 扩展组件 {componentDidMount = () =>this.props.setBackgroundImage(`./img/exhibit-one/${this.getWall()}/bg.jpg`)getWall = () =>{//这部分不重要.这是一个确定我在哪面墙上的函数,以便设置//正确的图像.}使成为() {返回 <ComposedComponent/>}}返回连接(空,动作)(ExhibitHoc);}

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

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

解决方案

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

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

const ExhibitWallOne = ExhibitHOC(WallOne);const ExhibitWallTwo = ExhibitHOC(WallTwo);..导出默认类 ExhibitOne 扩展组件 {..<路由路径={`${match.url}/wall-one`} 组件={ExhibitWallOne}/><路由路径={`${match.url}/wall-two`} component={ExhibitWallTwo}/>..}

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

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

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

Inside a small portion of my React/Redux/ReactRouterV4 application, I have the following component hierarchy,

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

Within the children of Exhibit, there are about 6 different possible routes that can be rendered as well. Don't worry, I will explain with some code.

Here is my Parent Exhibit Component:

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

Basically, all its does for its job is to display one of the exhibits subcomponents, and set a background image.

Here is one of the subcomponents, 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>
    )
  }
}

In order to cut down on typing, I decided to wrap the components in a Higher Order Component, whose purpose is to dispatch an action that will set the proper background image on the top level Exhibit parent component.

This is the Higher Order Component:

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

On initial load of ExhibitOne, I can see that the setBackgroundImage action creator executes twice by looking at Redux Logger in the console. My initial inclination to use componentDidMount was because I thought using it would limit the action creator to execute only once. Here is a screenshot of the log:

I think I might be misunderstanding how Higher Order Components work, or maybe its some type of React Router V4 thing? Anyways, any help would be greatly appreciated as to why this executes twice.

解决方案

The problem is that the component prop here is a function application, which yields a new class on each render. This will cause the previous component to unmount and the new one to mount (see the docs for react-router for more information). Normally you would use the render prop to handle this, but this won't work with higher-order components, as any component that is created with a HOC application during rendering will get remounted during React's reconciliation anyway.

A simple solution is to create your components outside the ExhibitOne class, e.g.:

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} />
          ..
}

Alternatively, depending on what the wrapper does, it might be possible to declare it as a normal component that renders {this.props.children} instead of the parameter <ComposedComponent/>, and wrap the components in each Route:

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

Note that you'll need to use render instead of component to prevent remounting. If the components don't use routing props, you could even remove {...props}.

这篇关于React 组件安装两次的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆