一次更改道具后,使用redux connect的已连接组件的子组件不会重新渲染

时间:2018-11-28 15:10:53

标签: javascript reactjs redux

我将应用程序的主导航菜单作为使用connect连接到redux存储的react组件。菜单组件也分为几部分,在部分内部有主题。菜单具有状态变量(在商店中),用于指示菜单是否必须折叠显示。我通过相应的操作更改了状态,我看到菜单外观发生了变化,但是各部分的外观没有变化。我将collapse的值作为道具传递,但是如果没有调用要相应渲染的菜单部分的render函数。

这是Menu和MenuSection组件的代码:

Menus.jsx

// modules/menu/components/Menu.jsx

import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as actions from '../actions'

import { MODULE_NAME } from '../constants'
import MenuSection from './MenuSection'

const propTypes = {
  menu: PropTypes.shape({
    sections: PropTypes.array,
    collapsed: PropTypes.bool,
    activeSection: PropTypes.number,
    inflatedSection: PropTypes.number,
    activeTopic: PropTypes.number,
  }).isRequired,
  actions: PropTypes.shape({
    toggle: PropTypes.func,
    activateSection: PropTypes.func,
    toggleSection: PropTypes.func,
  }).isRequired,
}

class Menu extends React.Component {

  constructor(props) {
    super(props)
  }

  toggleMenu = () => this.props.actions.toggleMenu()

  activateSection = (s, t) => this.props.actions.activateSection(s, t)

  toggleSection = (s) => this.props.actions.toggleSection(s)

  render() {
    const collapsed = this.props.menu.collapsed
    const sections = this.props.menu.sections.map((section, i) => {
      const {
        disabled = false,
        icon, text,
        route = { to: '#' },
        topics = [],
      } = section
      const active = this.props.menu.activeSection === i
      const inflated = this.props.menu.inflatedSection === i
      return (
        <MenuSection
          key={i}
          idx={i}
          to={route.to}
          {...{
            icon,
            text,
            topics,
            disabled,
            active,
            inflated,
          }}
          activate={this.activateSection}
          toggle={this.toggleSection}
          activeTopic={this.props.menu.activeTopic}
          collapsed={collapsed}
        />
      )
    })
    const classes = classNames('menu', { collapsed: collapsed })
    const size = collapsed ? 'small' : 'big' 
    return (
      <nav className={classes}>
        <img src={`${process.env.PUBLIC_URL}/imgs/logo-${size}.png`} />
        <ul>
          {sections}
        </ul>
      </nav>
    )
  }

}

Menu.propTypes = propTypes

const mapStateToProps = state => ({ [MODULE_NAME]: state.menu })

const mapDispatchToProps = dispatch => (
  { actions: bindActionCreators(actions, dispatch) }
)

export default connect(mapStateToProps, mapDispatchToProps)(Menu)

MenuSection.jsx

    // modules/menu/components/MenuSection.jsx

import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { NavLink, withRouter } from 'react-router-dom'
import classNames from 'classnames'

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faCaretDown, faCaretUp } from '@fortawesome/free-solid-svg-icons'

import MenuTopic from './MenuTopic'

const propTypes = {
  idx: PropTypes.number.isRequired,
  to: PropTypes.oneOfType([PropTypes.func, PropTypes.string]).isRequired,
  icon: PropTypes.object.isRequired,
  text: PropTypes.string.isRequired,
  topics: PropTypes.arrayOf(PropTypes.object).isRequired,
  activate: PropTypes.func.isRequired,
  toggle: PropTypes.func.isRequired,
  disabled: PropTypes.bool.isRequired,
  active: PropTypes.bool.isRequired,
  inflated: PropTypes.bool.isRequired,
  activeTopic: PropTypes.number.isRequired,
  collapsed: PropTypes.bool.isRequired
}

class MenuSection extends Component {

  constructor(props) {
    super(props)
    this.onClick = this.onClick.bind(this)
    this.activate = this.activate.bind(this)
  }

  shouldComponentUpdate(nextProps) {
    const { active, inflated, activeTopic } = nextProps;
    return (this.props.active !== active)
      || (this.props.inflated !== inflated) || (this.props.activeTopic !== activeTopic)
  }

  onClick() {
    if (this.props.topics.length > 0) {
      this.props.toggle(this.props.idx)
    } else {
      this.props.activate(this.props.idx, this.props.activeTopic)
    }
  }

  activate(t) {
    this.props.activate(this.props.idx, t)
  }

  render() {
    const { idx, to, icon, text, topics, disabled, active, inflated, activeTopic, collapsed } = this.props
    const items = (inflated ?
      topics.map((topic, i) => (
        <MenuTopic key={i} idx={i} 
          to={topic.route.to} text={topic.text} onClick={this.activate}
          active={active && (activeTopic === i)} section={idx} />))
      : []);
    const classes = classNames({ disabled, active, inflated })
    return (
      <li className={classes}>
        <NavLink to={to} activeClassName="active" onClick={this.onClick}>
          <FontAwesomeIcon icon={icon}  pull="left"/>
          {!collapsed &&
            <React.Fragment>
              {' '}
              {text}
              {topics.length > 0 &&
                <FontAwesomeIcon icon={ inflated ? faCaretUp : faCaretDown } pull="right" />
              }
            </React.Fragment>
          }
        </NavLink>
        {(!collapsed) && inflated &&
          <ul>
            {items}
          </ul>
        }
      </li>)
  }

}

MenuSection.propTypes = propTypes

export default withRouter(MenuSection)

有什么想法吗?我对react-redux应用程序有些陌生,所以也许我错过了一些东西。

谢谢!

1 个答案:

答案 0 :(得分:0)

它似乎不重新呈现的原因似乎是由于menuSection的shouldComponentUpdate。按照书面规定,仅在props.active,props.inflated或props.activeTopic更改时,才会重新渲染menuSection。因此,如果props.collapsed发生更改,但没有其他操作,则跳过渲染。