У меня есть объект класса 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
@RenanSouza Я действительно заметил, что набрал это таким образом, и исправил это. Та же проблема. Хотя сообщение отредактировано для ясности.
Я смотрю со своего телефона, поэтому мне трудно помочь, но синтаксис здесь выглядит неправильно: handleUpdateEntity(this.props.updateProperty(id, name, value) {
вы можете удалить всю эту функцию и вызвать свойство непосредственно в обработчике изменений, например const { updateProperty } = this.props;
, затем onChange = {e => updateProperty(id, name, e.target.value)}
Кроме того, key
, который у вас есть на входе, выглядит не уникальным, поскольку это объект, и вы используете его в цикле, сделайте ключ именем, свойством, индексом или чем-то уникальным, что не изменится
@RenanSouza На самом деле я создаю руководство из 16 символов, но для краткости я не включил эту функцию. Это уже немного затянуто XD
Хорошо, просто убедитесь, что ключ уникален для каждого ввода, а не только для каждой сущности, и что ключ не меняется при каждом рендеринге.
Если вам все еще нужна помощь с этим, пожалуйста, добавьте пример codeandbox, чтобы нам было легче его отлаживать.
@NemanjaLazarevic готово! Спасибо за предложение
Попробуйте отправлять фактическое входное значение каждый раз, когда оно изменяется, например:
<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)
Надеюсь, вы действительно пропустили мой комментарий и не используете его :)
Я действительно заметил, что набрал это таким образом, и исправил это. Моя проблема так и не решена :/
Извините, Ренан, наш комментарий / ответ должен быть опубликован одновременно, потому что сообщение было пустым, когда я впервые попал на страницу.
Итак, после нескольких дней переделки этого я обнаружил, что проблема заключается в правильном изменении значений вложенных объектов. В своем коде я попытался изменить значения вложенного объекта 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%, почему это так, но это так. Если у кого-то есть твердое понимание этого, пожалуйста, протяните руку и прокомментируйте!
В классе PropertiesWindow у вас есть
onChange = {e => this.handleUpdateEntity(this.selectedEntity.id, name, property.value)}
Я предполагаю, что это вызывает проблему, поскольку вы повторно используете значение свойства. Попробуйте переключитьproperty.value
наe.target.value
, чтобы вы могли получить фактическое значение на входе.