Реагировать, теряя фокус на поле ввода (компонент добавляется динамически)

Стек: 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

И текстовое поле, используемое здесь:

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.info(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.info(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) ...

Любая помощь или комментарии ДЕЙСТВИТЕЛЬНО приветствуются!

Вы сказали the whole component named MultipleInputChoiceList is mounted again, это означает, что проблема может быть где-то выше, чем MultipleInputChoiceList. Не могли бы вы прикрепить компонент, который отображает MultipleInputChoiceList?

Yozi 15.05.2018 12:04

Готово, он отображается через ConditionalRenderingHOC, который я опубликовал, после этого все значения полностью фиксируются, и вызывается единственная строка: render() { const SelectProofs = ConditionalRenderingHOC(MultipleInputChoiceList, this.state.form.doProofs)

SephiLink 15.05.2018 12:17

Я просто подумал о чем-то: возможно ли, что, поскольку я использую тот же объект Json в качестве реквизита для MultipleInputChoiceList и для обновления моих данных -> `this.state.form.doProofs`, который принимает состояние Redux в качестве состояния изменит ли он будет автоматически перерисовывать все компоненты, которые от него зависят?

SephiLink 15.05.2018 12:25

Да, может быть. Я считаю, что вам нужно отладить ConditionalRenderingHOC и выяснить, почему MultipleInputChoiceList был размонтирован и снова смонтирован.

Yozi 15.05.2018 12:51
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
4
2 150
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

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

Другие вопросы по теме