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

时间:2018-05-15 09:56:28

标签: javascript reactjs redux react-redux react-component

Stack:React16,ES6,Redux

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

我能够在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方法(MultipleInputChoiceList.js)......

任何帮助或评论真的很感激!

1 个答案:

答案 0 :(得分:0)

截至目前,我还没有真正回答这个问题,因为这是我数据中的结构问题(当通过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>
    )
  }

如果有人有其他解决方案,我会很高兴听到它!