Стек: 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) ...
Любая помощь или комментарии ДЕЙСТВИТЕЛЬНО приветствуются!
Готово, он отображается через ConditionalRenderingHOC, который я опубликовал, после этого все значения полностью фиксируются, и вызывается единственная строка: render() { const SelectProofs = ConditionalRenderingHOC(MultipleInputChoiceList, this.state.form.doProofs)
Я просто подумал о чем-то: возможно ли, что, поскольку я использую тот же объект Json в качестве реквизита для MultipleInputChoiceList и для обновления моих данных -> `this.state.form.doProofs`, который принимает состояние Redux в качестве состояния изменит ли он будет автоматически перерисовывать все компоненты, которые от него зависят?
Да, может быть. Я считаю, что вам нужно отладить ConditionalRenderingHOC и выяснить, почему MultipleInputChoiceList был размонтирован и снова смонтирован.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


На данный момент я не пришел к настоящему ответу на этот вопрос, поскольку это структурная проблема в моих данных (при обновлении поля с помощью действия 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>
)
}
Если у кого-нибудь есть другое решение, я с удовольствием его выслушаю!
Вы сказали
the whole component named MultipleInputChoiceList is mounted again, это означает, что проблема может быть где-то выше, чем MultipleInputChoiceList. Не могли бы вы прикрепить компонент, который отображает MultipleInputChoiceList?