对道具更改做出反应重新渲染

Posted

技术标签:

【中文标题】对道具更改做出反应重新渲染【英文标题】:React rerender on prop change 【发布时间】:2019-03-14 05:00:51 【问题描述】:

我的问题是当我在 redux 存储中更改状态并基于此状态安装或卸载组件时。代码如下所示:

class Main extends Component 

  render() 
    const  dropdownState  = this.props;
    return (
      <div>
        <SecondHeadBar />
        <div className="main">
          <Switch>
            <Route exact path='/' component=withRouter(WebsiteIndex)/>
            <Route path='/track/:trackid' component=withRouter(MssTrack)/>
            <Route path='/album/:albumid' component=withRouter(Container.AlbumContainer)/>
            <Route path='/profile/:userName' component=withRouter(MssUser)/>
            <Route path='/upload/:albumid' component=withRouter(MssUploadTemplate)/>
            <Route path='/upload' component=withRouter(MssUploadTemplate)/>
            <Route path='/admin' component=withRouter(ControlCenter)/>
            <Route path='/kategorie' component=withRouter(Category) exact/>
            <Route path='/kategorie/:catName' component=withRouter(Folder)/>
            <Route path='/notFound' component=withRouter(NotFound)/>
            <Route path='/meine-eintraege' component=withRouter(Container.MyEntriesContainer)/>
          </Switch>
        </div>
        dropdownState ? <DownloadDropdown /> : ''
      </div>
    );
  


function mapStateToProps(state) 
  return 
    dropdownState: state.collection.dropdownState
  ;


function mapDispatchToProps(dispatch) 
  return 
    dispatch
  ;



export default connect(mapStateToProps, mapDispatchToProps)(Main);

每当道具dropdownState 改变时。并且组件DownloadDropdown 被挂载,然后Main 组件中的所有内容都被重新渲染。于是内容一闪而过。

【问题讨论】:

你的问题到底是什么? 如果不清楚,请抱歉。每当道具 dropdownState 发生变化时。并且组件 DownloadDropdown 会在 render() 函数重新渲染的所有内容中安装。所以内容会闪烁,因为它正在从 API 异步加载 @ArnaudChrist 我稍微改变了这个问题。希望这能让它更清楚。 与其卸载并重新安装DownloadDropdown,不如将​​dropdownState 作为道具传递给它,然后让它决定何时需要重新渲染/调用服务器。 @JoeClay 我也有这个想法,但我不知道这是否是一个不好的做法,因为这样会一直安装 DownloadDropdown。 【参考方案1】:

最简单的解决方案是让&lt;DownloadDropdown /&gt; 成为连接到Redux 的容器组件,并且始终保持挂载状态虽然不可见。然后您可以使用HOC 或始终安装且可见的东西(如&lt;SecondHeadBar /&gt;)并将其连接到切换DownloadDropdown 可见性的Redux action creator。换句话说,将 Redux 隔离到两个组件,而不是整个路由树。

工作示例:https://codesandbox.io/s/yw4m7yz8r1(浏览路线并点击顶部的“下载时间表”链接!)

我不确定您是如何触发挂载/卸载的,但让我们继续通过按钮切换它:

SecondHeadBar.js

import React,  Component  from 'react';
import  connect  from 'react-redux';
import  handleDropdown  from '../actions';

class SecondHeadBar extends Component 
  state = ...

  componentDidMount = () =>  ... 

  render = () => (
    <div>
      ...
      <button onClick=this.props.handleDropdown>Toggle Dropdown</button>
      ...
    </div>
  )


export default connect(null,  handleDropdown )(SecondHeadBar)

下载Dropdown.js

import React,  Component  from 'react';
import  connect  from 'react-redux';

class DownloadDropdown extends Component 
  state =  ... 

  componentDidMount = () =>  ... 


  render = () => (
    this.props.isVisible
      ? <div>I'm visible!</div>
      : null
  )


export default connect(state => ( isVisible: state.dropdown ))(DownloadDropdown)

actions.js

import  TOGGLE_DROPDOWN  from '../types'


export const handleDropdown = () => (
  type: TOGGLE_DROPDOWN
)

reducers.js

import  TOGGLE_DOWN  from '../types';

...

const dropdownReducer = (state=false,  type, payload ) => 
  switch(type) 
    case TOGGLE_DROPDOWN: return !state
    default: return state
  


export default = combineReducer(
  ...
  dropdown: dropdownReducer
  ...
)

routes.js

const Main = () => (
  <div>
    <SecondHeadBar />
    <div className="main">
      <Switch>
        <Route exact path='/' component=withRouter(WebsiteIndex)/>
        <Route path='/track/:trackid' component=withRouter(MssTrack)/>
        <Route path='/album/:albumid' component=withRouter(Container.AlbumContainer)/>
        <Route path='/profile/:userName' component=withRouter(MssUser)/>
        <Route path='/upload/:albumid' component=withRouter(MssUploadTemplate)/>
        <Route path='/upload' component=withRouter(MssUploadTemplate)/>
        <Route path='/admin' component=withRouter(ControlCenter)/>
        <Route path='/kategorie' component=withRouter(Category) exact/>
        <Route path='/kategorie/:catName' component=withRouter(Folder)/>
        <Route path='/notFound' component=withRouter(NotFound)/>
        <Route path='/meine-eintraege' component=withRouter(Container.MyEntriesContainer)/>
      </Switch>
    </div>
    <DownloadDropdown/>
  </div>
);

export default Main;

现在,当用户点击&lt;SecondHeadBar/&gt; 中的“切换下拉菜单”按钮时,它会更新&lt;DownloadDropdown/&gt; 的可见性,而不会影响您的路由树。

【讨论】:

【参考方案2】:

我认为你可以使用这个生命周期方法来检查。

 static getDerivedStateFromProps(nextProps, prevState) 
        if (nextProps.name !== prevState.name) 
            return  name: nextProps.name;
        
    

或者对于旧版本检查componentwillreceiveProps 并停止重新渲染。

【讨论】:

以上是关于对道具更改做出反应重新渲染的主要内容,如果未能解决你的问题,请参考以下文章

将新的商店状态放入道具后,反应不会重新渲染

反应父母对孩子道具传递而不重新渲染

在我滚动之前,react-virtualized 列表项不会使用更改的道具重新渲染

反应只重新渲染一个孩子

Vue 没有对计算的 props 更改做出反应

对路由器更改做出反应重新安装