React 失去对输入字段的关注(动态添加的组件)

React losing focus on input field (component added dynamically)

堆栈:React16、ES6、Redux

我目前无法弄清楚这里出了什么问题。这里的目标是在单击添加按钮时动态添加无限数量的组件(一个接一个)。 我需要让它们出现,如果可能的话成对或更多,例如如果我点击“添加”按钮,每次应该出现 2 个字段(一个 select 字段和一个文本字段同时出现,例如)

在 Redux 的帮助下,我能够让组件出现,而且我还能够正确地管理数据(一切都在应用程序的背面连接)

这里的问题:

尝试在输入字段中键入文本时,它总是失去焦点。我已经看到,每次我更新道具时,都会再次安装名为 MultipleInputChoiceList 的整个组件,并且每个字段都会重新创建。这就是我需要解决的问题:

编辑:MultipleInputChoiceList 组件是通过条件渲染 HOC 安装的(它需要一些值并检查它们是否为真,如果为真,它会在不触及整个表单的情况下渲染组件)

ConditionalRenderingHOC.js

import  React from 'react'
import {connect} from 'react-redux'
import _ from 'lodash'

const mapStateToProps = state => {
  return {
    form: state.form.form
  }
}

const mapDispatchToProps = dispatch => {
    return {
    }
  }

  /**
   * HOC Component to check conditional rendering on form component, using requireField property
   * To be enhanced at will
   */
  export default (WrappedComponent, formItem = {}) => {
    class ConditionalRenderingHOC extends React.Component {
        componentWillMount() {
            //Check if all informations are available
            if (formItem.requireField !== undefined) {
                const requireField = formItem.requireField
                if (requireField.value !== undefined && 
                    requireField.name !== undefined &&
                    requireField.field !== undefined &&
                    requireField.property !== undefined) {
                        //If everything's here let's call canBeRendered
                        this.canBeRendered()
                    }
            }
        }       

        //Check if the count of fetched values is directly linked to the number of fetched config asked, if true, return the same number
        canBeRendered() {
            formItem.requireField.isRendered = false
            let required = formItem.requireField
            let isEqual = false
            if (this.props.form[required.field] !== undefined) {
                let field = this.props.form[required.field]
                _.forEach(field.value, (properties, index) => {
                    if (properties[required.name] !== undefined) {
                        if (properties[required.name] === required.value) {
                            if (properties[required.property] === required.isEqualTo) {
                                formItem.requireField.isRendered = true
                                isEqual = true
                            }
                        }
                    }
                })
            }

            return isEqual
        }

        render() {
            let isConditionMet = this.canBeRendered() 
            let render = null
            if (isConditionMet === true) {
                render = <WrappedComponent items={formItem}/>
            } 

        return (<React.Fragment>
            {render}
          </React.Fragment>)
        }
    }

    return connect(mapStateToProps, mapDispatchToProps)(ConditionalRenderingHOC)
}

代码

    //Essentials
import React, { Component } from 'react'
import _ from 'lodash'
//Material UI
import TextField from 'material-ui/TextField'
import IconButton from 'material-ui/IconButton'
import AddBox from 'material-ui/svg-icons/content/add-box'
//Components
import SelectItemChoiceList from '../form/SelectItemChoiceList'
import TextFieldGeneric from './TextFieldGeneric'

//Redux
import { connect } from 'react-redux'
import { createNewField } from '../../../actions/formActions'

const mapStateToProps = (state) => {
  return {
    form: state.form.form
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    createNewField: (field, state) => dispatch(createNewField(field, state))
  }
}

class MultipleInputChoiceList extends Component {
  constructor(props) {
    super(props)
    this.state = {
      inputList: [],
    }
  }

  onAddBtnClick() {
    const name = this.props.items.name
    /**Create a new field in Redux store, giving it some datas to display */
    this.props.createNewField(this.props.form[name], this.props.form)  
  }

  render() {
    const name = this.props.items.name
    /**I think the error is around this place, as it always re-render the same thing again and again */
    const inputs = this.props.form[name].inputList.map((input, index) => {
      switch(input) { 
      case 'selectfield': { 
        return React.createElement(SelectItemChoiceList, {
          items: this.props.form[name].multipleField[index],
          key:this.props.form[name].multipleField[index].name
        })

      } 
      case 'textfield': { 
        return React.createElement(TextFieldGeneric, {
          items: this.props.form[name].multipleField[index],
          index:index,
          key:this.props.form[name].multipleField[index].name
        })
      } 
      default: { 
        break             
      } 
      } 
    })

    return (
      <div>
        <IconButton onClick={this.onAddBtnClick.bind(this)}>
          <AddBox />
        </IconButton>
        {inputs}
      </div>
    )
  }
}

const MultipleInputChoiceListRedux = connect(mapStateToProps, mapDispatchToProps)(MultipleInputChoiceList)

export default MultipleInputChoiceListRedux

以及此处使用的 TextField:

TextFieldGeneric.js

//Essentials
import React, { Component } from 'react';
//Components
import TextField from 'material-ui/TextField'
//Redux
import { connect } from 'react-redux'
import { validateField, isValid } from '../../../actions/formActions'

const mapStateToProps = (state) => {
  return {
    form: state.form.form
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    validateField: (field) => dispatch(validateField(field)),
    isValid: () => dispatch(isValid())
  }
}

class TextFieldGeneric extends Component {
  constructor(props) {
    super(props)
    this.state = {
      form: {},
      field: {},
      index: 0
    }
  }

  componentWillMount() {
    console.log(this.props)
    //first, let's load those dynamic datas before rendering
    let form = this.props.form
    let index = this.props.index
    /** Check if there's a correctly defined parent in form (taken from the name) */
    let matchName = /[a-zA-Z]+/g
    let origin = this.props.items.name.match(matchName)

    //form.company.value = this.getCompaniesFormChoice()
    this.setState({form: form, field: form[origin], index: index})
  }

  //setState and check validationFields if errors
  handleFieldChange(event){
    const name = event.target.name
    const value = event.target.value
    //Change value of state form field
    const item = this.props.items
    item.value = value
    //validate each fields
    this.props.validateField(item)
    //validate form
    this.props.isValid()

    event.preventDefault()
  }

  render() {
    const index = this.state.index
    console.log(index)
    return (
      <React.Fragment>
        <TextField
          key={index}
          floatingLabelText={this.state.field.multipleField[index].namefield}
          name={this.state.field.multipleField[index].namefield}
          floatingLabelFixed={true}
          value = {this.state.field.multipleField[index].value}
          onChange = {this.handleFieldChange.bind(this)}
          errorText={this.state.field.multipleField[index].error === 0 ? '' : this.state.field.multipleField[index].error}
        />
      </React.Fragment>
    )
  }
}

const TextFieldGenericRedux = connect(mapStateToProps, mapDispatchToProps)(TextFieldGeneric)
export default TextFieldGenericRedux

我也明白部分问题出在父级的 render 方法上 class (MultipleInputChoiceList.js) ...

非常感谢任何帮助或评论!

到目前为止,我还没有真正回答这个问题,因为这是我数据中的一个结构问题(当通过 Redux 操作更新字段时,它会重新呈现整个组件)。也许将这些数据存储在其他地方会是更好的选择。

我只在该字段上使用了一个 onBlur 方法,它取消了我想对每个用户输入进行的验证,但目前我想不出另一个可行的解决方案。

到目前为止,TextFieldGeneric.js 看起来像这样:

//setState and check validationFields if errors
  handleFieldChange(event){
    this.setState({value: event.target.value})

    event.preventDefault()
  }

  handleValidation(event){
    const value = this.state.value
    //Change value of state form field
    const item = this.props.items
    item.value = value
    //validate each fields
    this.props.validateField(item)
    //validate form
    this.props.isValid()
  }

  render() {
    return (
      <React.Fragment>
        <TextField
          name='name'
          floatingLabelFixed={true}
          value = {this.state.value}
          onChange = {this.handleFieldChange.bind(this)}
          onBlur = {this.handleValidation.bind(this)}
        />
      </React.Fragment>
    )
  }

如果有人有其他解决方案,我很乐意听取!