Реакция потери фокуса на поле ввода (компонент добавляется динамически) - PullRequest
0 голосов
/ 15 мая 2018

Стек: React16, ES6, Redux

В настоящее время я не могу понять, что здесь не так. Цель здесь - динамически добавлять бесконечное количество компонентов (один за другим) при нажатии на кнопку добавления. Мне нужно, чтобы они появились, если возможно, парой или более, например, если я нажму на кнопку «ДОБАВИТЬ», то каждый раз будут появляться 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

Я также понимаю, что часть проблемы заключается в методе рендеринга родительского класса (MultipleInputChoiceList.js) ...

Любая помощь или комментарии очень ценятся!

1 Ответ

0 голосов
/ 15 мая 2018

На данный момент я не пришел к реальному ответу на этот вопрос, поскольку это структурная проблема в моих данных (при обновлении поля с помощью действия 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>
    )
  }

Если у кого-нибудь есть другое решение, я с удовольствием его услышу!

...