reactjs - 使用动态映射内容替换静态标记时的onClick错误

时间:2017-06-02 23:54:14

标签: javascript reactjs

Reactjs项目 - 点击切换链接时出现以下错误。

“警告:setState(...):无法在现有状态转换期间更新(例如在render或其他组件的构造函数中)。渲染方法应该是props和state的纯函数;构造函数方 - 效果是一种反模式,但可以移到componentWillMount。“

现在我刚刚更换了运行正常的静态标记。

这就是静态标记的样子

              <p className='text--white grid__row--offset--15 footer-text'>
                <Link to={urls[1]} className={`text--white footer-text ${this.props.active_language === 'de' ? activeLang : alternativeLang}`} onClick={this.changeLanguageToGerman}>DE</Link>
                &nbsp;&nbsp;&#124;&nbsp;&nbsp;
                <Link to={urls[0]} className={`text--white footer-text ${this.props.active_language === 'en' ? activeLang : alternativeLang}`} onClick={this.changeLanguageToEnglish}>EN</Link>
              </p>

- 动态标记看起来像这样 -

                         {
                           item.children.map(function (child, j) {
                             return (
                               <span key={j}>
                                 <Link className={'text--white footer-text transition ' + (props.active_language === child.title.toString().toLowerCase() ? activeLang : alternativeLang)} to={urls[j]} onClick={(child.title.toString().toLowerCase() === 'en' ? changeLanguageToEnglish : changeLanguageToGerman)} >{child.title}</Link>
                                 {j === 0 && <span className='text--white'> | </span>}
                               </span>
                             )
                           })
                         }

我已经在渲染中添加了函数,所以在带有标记的返回上方

  render () {
    const changeLanguageToEnglish = this.changeLanguageToEnglish()
    const changeLanguageToGerman = this.changeLanguageToGerman()

所以这里有一些范围问题 - 但如果我把它留作this.function - 它可能找不到它,因为它嵌套在各种循环中?

完整的例子。

import React from 'react'
import { Link, withRouter } from 'react-router-dom'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { selectLanguage, getLangDetails } from '../../actions/action_language'
import langObject from './Footer.lang'
import linkTreeObject from '../HeaderLanding/Header.lang'
import HeartIcon from './img/HeartIcon'

import './footer.scss'

// ==================================================================================================
// ==================================================================================================
//
// this is a redux container

class Footer extends React.Component {
  constructor (props) {
    super(props)
    this.changeLanguageToGerman = this.changeLanguageToGerman.bind(this)
    this.changeLanguageToEnglish = this.changeLanguageToEnglish.bind(this)
  }
  // ==================================================================================================
  //
  // change language to German called from footer link
  //
  changeLanguageToGerman () {
    this.props.selectLanguage('de')
  }
  // ==================================================================================================
  //
  // change language to German called from footer link
  //
  changeLanguageToEnglish () {
    this.props.selectLanguage('en')
  }
  // ==================================================================================================
  //
  getUrl (pairUrl, currentLng, enMenu, deMenu, obj) {
    for (let k in obj) {
      if (!obj.hasOwnProperty(k)) continue
      if (obj[k].link === pairUrl) {
        if (currentLng === 'de') {
          return enMenu[k].link // get en link equivlant
        } else {
          return deMenu[k].link // get de link equivlant
        }
      } else {
        if (!obj[k].hasOwnProperty('children') || obj[k].children.length <= 0) continue
        let ret = this.getUrl(pairUrl, currentLng, enMenu[k].children, deMenu[k].children, obj[k].children)
        if (typeof ret !== 'undefined') return ret
      }
    }
  }
  // ==================================================================================================
  //
  getLanguagePair (currentLng, pairUrl, languageObj) {
    //  'find url in json tree'
    let enMenu = languageObj.langs[1].lines.menu
    let deMenu = languageObj.langs[0].lines.menu

    let obj = {}
    // find position in tree
    if (currentLng === 'de') {
      obj = deMenu
    } else {
      obj = enMenu
    }

    return this.getUrl(pairUrl, currentLng, enMenu, deMenu, obj)
  }
  // ==================================================================================================
  //
  fetchFooterUrls () {
    let deUrl = ''
    let enUrl = ''

    if (this.props.active_language === 'de') {
      deUrl = this.props.location.pathname
      enUrl = this.getLanguagePair(this.props.active_language, this.props.location.pathname, linkTreeObject)
      if (!enUrl) {
        enUrl = this.getLanguagePair(this.props.active_language, this.props.location.pathname, langObject)
      }
    } else {
      enUrl = this.props.location.pathname
      deUrl = this.getLanguagePair(this.props.active_language, this.props.location.pathname, linkTreeObject)
      if (!deUrl) {
        deUrl = this.getLanguagePair(this.props.active_language, this.props.location.pathname, langObject)
      }
    }

    if (!enUrl) {
      enUrl = '/en'
    }

    if (!deUrl) {
      deUrl = '/de'
    }

    return [enUrl, deUrl]
  }
  // ==================================================================================================
  //
  render () {
    let activeLang = 'language--active'
    let alternativeLang = 'language--hover'
    // take the  current lang state from redux and the json lang files and sends it off to receive the appropriate language object (de/en)
    const lang = getLangDetails(this.props.active_language, langObject)
    const urls = this.fetchFooterUrls()
    const props = this.props
    const changeLanguageToEnglish = this.changeLanguageToEnglish
    const changeLanguageToGerman = this.changeLanguageToGerman

    console.log(urls)
    // get active language, get current location -- create an array for en and de urls -- determine if current url is german or english -- find its counter part from the header.lang.json
    return (
      <div>
        {/* ---- FOOTER LARGE ---- */}
        <footer className='main-footer show-for-large-up'>
          <div className='row'>
            {
             lang.menu.reduce((m, k, i) => {
               m.push(k)
               if (i === lang.menu.length - 1) {
                 m = [m.slice(0, 1), m.slice(1)]
               }
               return m
             }, []).map((grouped, index) => (
               <div key={index} className={index === 0 ? 'main-footer__left' : 'main-footer__right'}>
                 {
                   <div className='row grid__row--offset--30'>
                     {
                       grouped.map((item, j) =>
                         <div key={j} className={(index === 0 && j === 0 ? 'large-45 large-centered' : 'large-14 large-offset-5') + ' columns'}>
                           <h2 className={'text--uppercase footer-text ' + (index === 0 ? 'text--white' : 'text--light-blue')}>{item.title}</h2>
                           {
                             item.switch
                               ? <p className='text--white grid__row--offset--15 footer-text'>
                                 {
                                   item.children.map(function (child, j) {
                                     return (
                                       <span key={j}>
                                         <Link className={'text--white footer-text transition ' + (props.active_language === child.title.toString().toLowerCase() ? activeLang : alternativeLang)} to={urls[j]} onClick={(child.title.toString().toLowerCase() === 'en' ? changeLanguageToEnglish.bind(this) : changeLanguageToGerman.bind(this))} >{child.title}</Link>
                                         {j === 0 && <span className='text--white'> | </span>}
                                       </span>
                                     )
                                   })
                                 }
                               </p>
                               : item.children.map(function (child, j) {
                                 return (
                                   <div key={j} className={(j === 0 ? ' grid__row--offset--15' : '')}>
                                     <Link className={'footer-text transition ' + (index === 0 ? '' : 'text--white')} to={child.link}>{child.title}</Link>
                                   </div>
                                 )
                               })
                           }
                           <div className='vert-line--light-blue footer-bars' />
                         </div>
                       )
                     }
                   </div>
                 }
                 {index !== 0
                  ? <div className='row grid__row--offset--30'>
                    <div className='large-15 large-centered columns'>
                      <p className='text--white text--center footer-text'>Made with&nbsp;
                        <span>
                          <HeartIcon />
                        </span>&nbsp;in Munich.
                      </p>
                    </div>
                  </div>
                  : ''
                }
               </div>
              ))
            }
          </div>
        </footer>
      </div>
    )
  }
}
// ==================================================================================================
// ==================================================================================================
//
// necessary to map state props
//
const mapStateToProps = (state) => {
  return {
    active_language: state.active_language
  }
}
// ==================================================================================================
// ==================================================================================================
//
// anything returned from this function will end up as props on the container
//
function mapDispatchToProps (dispatch) {
  // whenever selectLanaguage is called it gets send to all the reducers
  // bind selectLanguage is connected to the action creator to dispatch the action to the reducer
  return bindActionCreators({selectLanguage: selectLanguage}, dispatch)
}

const { string, func, object } = React.PropTypes
Footer.propTypes = {
  deURL: string,
  enURL: string,
  selectLanguage: func,
  active_language: string,
  location: object
}

// this connects the container to redux
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Footer))

1 个答案:

答案 0 :(得分:0)

以下函数似乎在渲染期间更新组件状态。你不应该调用它,因为它会更新触发该错误消息的状态。 (即改为:

  render () {
    const changeLanguageToEnglish = this.changeLanguageToEnglish
    const changeLanguageToGerman = this.changeLanguageToGerman

如果要在没有提到this问题的情况下将状态更新函数传递给子组件,可以使用bind或将函数定义为成员变量而不是方法。

  1. 使用bind

                 {
                   item.children.map(function (child, j) {
                     return (
                       <span key={j}>
                         <Link onClick={(child.title.toString().toLowerCase() === 'en' ? changeLanguageToEnglish.bind(this) : changeLanguageToGerman.bind(this))} >{child.title}</Link>
                         {j === 0 && <span className='text--white'> | </span>}
                       </span>
                     )
                   })
                 }
    
  2. 将其定义为成员变量

    class SomeComponent extends React.Component {
    
      changeLanguageToGerman = () => {
    
      }
      ...
      render() {
    
      }
    }