Как правильно привязать вложенные объекты к текстовым вводам с помощью избыточности

У меня есть объект класса Entities, который добавляет себя к редуктору сущностей, используя идентификатор в качестве своего ключа. В свойствах другого класса мне нужно использовать каждое из свойств объекта сущности для создания текстовых входов.

Проблема, с которой я сталкиваюсь, заключается в том, что когда я обновляю свое текстовое поле ввода, оно берет первый введенный мной ключ и делает его значением, но каждый раз переписывает полное значение, поэтому я всегда получаю односимвольное значение.

У меня есть класс Сущности:

import React from 'react';
import { connect } from 'react-redux';
import { updateSelected, addEntity } from '../actions';

class Entity extends React.PureComponent {
  constructor(props) {
    super(props);
    this.identityContainer = {
      id: "abcdef",
      type: "person",
      properties: {
        name: {
          label: "Name", 
          type: "string", 
          value: "", 
          optional: true
        },
        favorite_icecream: {
          label: "Favorite Ice Cream", 
          type: "string", 
          value: "", 
          optional: true
        }
      }
    }
    this.props.addEntity(this.identityContainer);
  }
  handleSelected() {
    this.props.updateSelected(this.identityContainer.id)
  }
  render() {
    return (
        <div onMouseUp = {() => this.handleSelected()}></div>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    selectedEntity: state.selectedEntity
  }
}
const mapDispatchToProps = () => {
  return {
    updateSelected,
    addEntity
  }
}
export default connect(mapStateToProps, mapDispatchToProps())(Entity);

Теперь вот мой класс PropertiesWindow:

import React, { Component } from 'react';
import { connect } from 'react-redux';
import { updateProperty } from '../actions';

class PropertiesWindow extends Component {
    get selectedEntity() {
        return this.props.entities[this.props.selectedEntity];
    }
    handleUpdateEntity(id, name, value) {
        this.props.updateProperty(id, name, value);
    }
    createListItems() {
        for (const [name, property] of Object.entries(this.selectedEntity.properties)) {
            return (
                <li key = {this.props.selectedEntity + property.name} className = "text-input-container">
                    <label htmlFor = {this.props.selectedEntity}>{property.label}</label>
                    <input 
                        type = "text" 
                        id = {this.props.selectedEntity} 
                        name = {name}
                        value = {property.value}
                        onChange = {e => this.handleUpdateEntity(this.selectedEntity.id, name, e.target.value)}
                    />
                </li>
            )
        }
    }
    render() {
        return (
            <ul>
                {this.createListItems()}
            </ul>
        )
    }
}

const mapStateToProps = (state) => {
    return {
        selectedEntity: state.selectedEntity,
        entities: state.entities
    }
}
const mapDispatchToProps = () => {
  return {
    updateProperty
  }
}

export default connect(mapStateToProps, mapDispatchToProps())(PropertiesWindow);

действия:

export const updateSelected = (id) => {
    return {
        type: 'SELECTED_ENTITY_UPDATED',
        payload: id
    }
}
export const addEntity = (entity) => {
    return {
        type: 'ENTITY_ADDED',
        payload: {...entity}
    }
}
export const updateProperty = (id, property, value) => {
    return {
        type: 'PROPERTY_CHANGED',
        payload: {id, property, value}
    }
}

редукторы:

import {combineReducers} from 'redux';

export const selectedEntityReducer = (state = null, action) => {
    switch (action.type) {
        case 'SELECTED_ENTITY_UPDATED':
            return action.payload;
        default: 
            return state;
    }
}

export const entitiesReducer = (state = {}, action) => {
    switch (action.type) {
        case 'ENTITY_ADDED':
            state[action.payload.id] = action.payload;
            return state;
        case 'PROPERTY_CHANGED':
            state[action.payload.id].properties[action.payload.property] = action.payload.value;
            return state;
        default: return state;
    }
}

export default combineReducers({
    selectedEntity: selectedEntityReducer,
    entities: entitiesReducer
});

После ввода в текстовое поле я регистрирую это из action.payload:
updateProperty: Object { id: "abcdef", property: "name", value: "a" }
updateProperty: Object { id: "abcdef", property: "name", value: "s" }
updateProperty: Object { id: "abcdef", property: "name", value: "d" }
updateProperty: Object { id: "abcdef", property: "name", value: "f" }

Если бы кто-то мог помочь мне понять, что я делаю неправильно, я был бы очень признателен. Я пытался решить эту проблему разными способами, включая использование массива для свойств и массива для сущностей вместо объектов. Ничто из того, что я пробовал, не имело никакого значения.

Обновлено:
Я создал codeandbox здесь: https://codesandbox.io/s/gracious-leakey-vzio6

В классе PropertiesWindow у вас есть onChange = {e => this.handleUpdateEntity(this.selectedEntity.id, name, property.value)} Я предполагаю, что это вызывает проблему, поскольку вы повторно используете значение свойства. Попробуйте переключить property.value на e.target.value, чтобы вы могли получить фактическое значение на входе.

Renan Souza 11.12.2020 23:22

@RenanSouza Я действительно заметил, что набрал это таким образом, и исправил это. Та же проблема. Хотя сообщение отредактировано для ясности.

jellyfith 11.12.2020 23:27

Я смотрю со своего телефона, поэтому мне трудно помочь, но синтаксис здесь выглядит неправильно: handleUpdateEntity(this.props.updateProperty(id, name, value) { вы можете удалить всю эту функцию и вызвать свойство непосредственно в обработчике изменений, например const { updateProperty } = this.props;, затем onChange = {e => updateProperty(id, name, e.target.value)}

Renan Souza 11.12.2020 23:36

Кроме того, key, который у вас есть на входе, выглядит не уникальным, поскольку это объект, и вы используете его в цикле, сделайте ключ именем, свойством, индексом или чем-то уникальным, что не изменится

Renan Souza 11.12.2020 23:38

@RenanSouza На самом деле я создаю руководство из 16 символов, но для краткости я не включил эту функцию. Это уже немного затянуто XD

jellyfith 11.12.2020 23:40

Хорошо, просто убедитесь, что ключ уникален для каждого ввода, а не только для каждой сущности, и что ключ не меняется при каждом рендеринге.

Renan Souza 11.12.2020 23:43

Если вам все еще нужна помощь с этим, пожалуйста, добавьте пример codeandbox, чтобы нам было легче его отлаживать.

Nemanja Lazarevic 13.12.2020 23:05

@NemanjaLazarevic готово! Спасибо за предложение

jellyfith 14.12.2020 20:58
Поведение ключевого слова "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) для оценки ваших знаний,...
1
8
119
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Попробуйте отправлять фактическое входное значение каждый раз, когда оно изменяется, например:

<input 
  type = "text" 
  id = {this.props.selectedEntity} 
  name = {name}
  value = {property.value}
  onChange = {e => this.handleUpdateEntity(this.selectedEntity.id, name, e.target.value)}
 />

Строка изменена вот эта:

onChange = {e => this.handleUpdateEntity(this.selectedEntity.id, name, e.target.value)

Надеюсь, вы действительно пропустили мой комментарий и не используете его :)

Renan Souza 11.12.2020 23:26

Я действительно заметил, что набрал это таким образом, и исправил это. Моя проблема так и не решена :/

jellyfith 11.12.2020 23:28

Извините, Ренан, наш комментарий / ответ должен быть опубликован одновременно, потому что сообщение было пустым, когда я впервые попал на страницу.

Nemanja Lazarevic 11.12.2020 23:36
Ответ принят как подходящий

Итак, после нескольких дней переделки этого я обнаружил, что проблема заключается в правильном изменении значений вложенных объектов. В своем коде я попытался изменить значения вложенного объекта properties, обратившись к нему с именами вычисляемых свойств, например

state[action.payload.id].properties[action.payload.property] = action.payload.value

Однако я обнаружил, что это не обновит состояние; или, если это так, это делает это таким образом, чтобы не запускать повторный рендеринг. Во многих других вопросах я видел, что переменная распространения должна использоваться для возврата совершенно нового объекта, который затем запускает повторную визуализацию. Возьмите этот код, например:

case ENTITY_UPDATED:
    const {id, name, value} = action.payload;
    return {
        ...state,
        [id]: {
            ...state[id],
            properties: {
                ...state[id].properties,
                [name]: {
                    ...state[id].properties[name],
                    value: value
                }
            }
        }
    }

Именно так нужно было изменить состояние, чтобы редукс зарегистрировал изменения. Я не уверен на 100%, почему это так, но это так. Если у кого-то есть твердое понимание этого, пожалуйста, протяните руку и прокомментируйте!

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