在每次更新时,React路由器都将卸载并重新安装子组件

时间:2020-04-22 15:49:32

标签: javascript reactjs react-router

我已经创建了一个组件,该组件是表单组件的包装:

class RestaurantEdit extends React.Component {
  constructor(props) {
    super(props)
    this.state = { waitingForRequest: false }
  }

  componentDidUpdate(prevProps) {
    const { currentRequestState } = this.props
    const { waitingForRequest } = this.state
    if (
      waitingForRequest &&
      prevProps.currentRequestState === requestStates.PENDING &&
      currentRequestState === requestStates.SUCCESS
    ) {
      this.props.history.goBack()
      this.setState({ waitingForRequest: false })
    }
  }

  handleSubmit = (restaurantInfos, restaurantId) => {
    const { editRestaurant } = this.props
    const { password, ...dataWithoutPassword } = restaurantInfos
    this.setState({ waitingForRequest: true })
    if (password === '') {
      editRestaurant(restaurantId, dataWithoutPassword)
    } else {
      editRestaurant(restaurantId, restaurantInfos)
    }
  }

  handleCancel = () => {
    this.props.history.goBack()
  }

  render() {
    const { id, data = {} } = this.props
    const { password, ...dataWithoutPassword } = data
    return (
      <RestaurantForm
        id={id}
        data={dataWithoutPassword}
        onCancel={this.handleCancel}
        onSubmit={this.handleSubmit}
        isPasswordRequired={false}
      />
    )
  }
}

const mapStateToProps = state => ({
  currentRequestState: restaurantSelectors.getRequestState(state)
})

const mapDispatchToProps = dispatch => ({
  editRestaurant: (restaurantId, restaurantInfos) => {
    dispatch(editRestaurantRequested(restaurantId, restaurantInfos))
  }
})

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(RestaurantEdit)

此组件安装在React路由器交换机的父级中:

    <Switch>
      <Route
        path="/restaurants/new"
        exact
        component={props => (
          <RestaurantCreate onSuccess={this.handleSuccess} {...props} />
        )}
      />
      <Route
        path="/restaurants/edit"
        component={props => {
          const id = props.location.search.split('=')[1]
          const data = currentCityRestaurants.find(resto => resto.id === id)
          return <RestaurantEdit id={id} data={data} {...props} />
        }}
      >
      </Route>
      <Route
        path="/restaurants"
        exact
        component={/* otherComponent */}
      />
    </Switch>

我在这里的大问题是,当我尝试调度一个动作(例如,editRestaurant(由mapDispatchToProps作为道具提供给RestaurantEdit的道具)时,商店正在更新,并且使组件卸下并重新安装。

此卸载操作将阻止this.setState({ waitingForRequest: true })中的componentDidUpdate完成,并且waitingForRequest始终为假

我注意到,如果我将组件作为Route的直接子代传递,则不会进行卸载/重新安装:

      <Route
        path="/restaurants/edit"
        exact
      >
        <RestaurantEdit …… />
      </Route>

但是我无法使用此解决方案,因为我需要访问location组件提供的historyRoute

当我将组件作为component的道具传递到Route时,有没有办法防止所有这些操作通过React Router卸载?

2 个答案:

答案 0 :(得分:1)

这就是在渲染周期中使用匿名函数时发生的情况。 React不知道您的匿名函数的输出与上次渲染的输出相同,这就是为什么要卸载和重新安装组件的原因。

话虽如此,您无需使用render prop函数即可访问Route prop。它们已经作为道具传递给了您的组件。

<Route
  path="/restaurants/edit"
  component={RestaurantEdit}
/>
const RestaurantEdit = (props) => {
  console.log(props.location, props.history)

  return ...
}

答案 1 :(得分:1)

如果要在路由中渲染元素时需要一些附加功能,则需要使用render关键字代替component

在您的示例中

<Route
    path="/restaurants/edit"
    component={props => {
      const id = props.location.search.split('=')[1]
      const data = currentCityRestaurants.find(resto => resto.id === id)
      return <RestaurantEdit id={id} data={data} {...props} />
    }}
  >

更改component关键字以呈现并使用函数返回修改后的组件

<Route
    path="/restaurants/edit"
    render={ props => {
      const id = props.location.search.split('=')[1]
      const data = currentCityRestaurants.find(resto => resto.id === id)
      return (<RestaurantEdit id={id} data={data} {...props} />)
    }}
  >

这可以方便地进行内联渲染和包装,而无需进行上述不必要的重新安装

https://reacttraining.com/react-router/web/api/Route/render-func