#javascript #reactjs #redux #react-redux
#javascript #reactjs #redux #реагировать-redux
Вопрос:
У меня есть объект класса Entities, который добавляет себя в редуктор сущностей, используя идентификатор в качестве своего ключа. В свойствах другого класса мне нужно использовать каждое из свойств объекта entity для создания текстовых вводов.
Проблема, с которой я сталкиваюсь, заключается в том, что когда я обновляю свое текстовое поле ввода, оно принимает первый введенный мной ключ и преобразует его в значение, но каждый раз переписывает полное значение, поэтому я всегда получаю значение в один символ.
У меня есть класс 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.полезная нагрузка:
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" }
Если бы кто-нибудь мог помочь мне понять, что я делаю неправильно, я был бы очень признателен. Я пытался решить эту проблему множеством разных способов, в том числе используя массив для свойств и массив для сущностей вместо объектов. Ничто из того, что я пробовал, не имело никакого значения.
РЕДАКТИРОВАТЬ:
я создал codesandbox здесь: https://codesandbox.io/s/gracious-leakey-vzio6
Комментарии:
1. В классе PropertiesWindow у вас есть
onChange={e => this.handleUpdateEntity(this.selectedEntity.id, name, property.value)}
, я предполагаю, что это вызывает проблему при повторном использовании значения свойства. Попробуйте переключитьсяproperty.value
e.target.value
, чтобы вы могли получить фактическое значение во входных данных2. Я смотрю со своего телефона, так что трудно чем-то помочь, но синтаксис здесь выглядит неправильно:
handleUpdateEntity(this.props.updateProperty(id, name, value) {
на самом деле вы можете удалить всю эту функцию и вызвать prop непосредственно в обработчике on change, какconst { updateProperty } = this.props;
тогдаonChange={e => updateProperty(id, name, e.target.value)}
3. Кроме
key
того, то, что у вас есть на входе, похоже, не уникально, поскольку это объект, и вы используете его в цикле, сделайте ключ именем, свойством, индексом или чем-то уникальным, что не изменится4. Хорошо, просто убедитесь, что ключ уникален для каждого ввода, а не только для каждого объекта, и что ключ не меняется при каждом рендеринге.
5. Если вам все еще нужна помощь в этом, пожалуйста, добавьте пример codesandbox, чтобы нам было легче его отлаживать.
Ответ №1:
Попробуйте отправлять фактическое входное значение каждый раз, когда оно изменяется, например:
<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)
Комментарии:
1. Я надеюсь, что вы действительно пропустили мой комментарий и не используете его 🙂
2. Я действительно заметил, что набрал это таким образом, и исправил это. Однако моя проблема все еще остается нерешенной :/
3. Извините, Ренан, наш комментарий / ответ, должно быть, был опубликован одновременно, потому что сообщение было пустым, когда я впервые попал на страницу.
Ответ №2:
Итак, после нескольких дней доработки я обнаружил, что проблема заключается в правильном изменении значений вложенных объектов. В моем коде я попытался изменить значения вложенного properties
объекта, обратившись к нему с помощью вычисляемых имен свойств, таких как
state[action.payload.id].properties[action.payload.property] = action.payload.value
Однако я обнаружил, что это не обновит состояние; или, если это произойдет, это будет сделано таким образом, чтобы не запускать повторный рендеринг. Во многих других вопросах я видел, что переменная spread должна использоваться для возврата совершенно нового объекта, который затем запускает повторный рендеринг. Возьмем этот код для примера:
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
}
}
}
}
Это способ изменения состояния, который необходимо было изменить, чтобы redux зарегистрировал изменения. Я не уверен на 100%, почему это так, но это так. Если кто-то хорошо разбирается в этом, пожалуйста, свяжитесь с нами и прокомментируйте!