#javascript #reactjs #typescript
Вопрос:
У меня есть компонент React, в котором есть некоторые поля формы:
<TextField
label="Description"
id="description"
value={company.companyDescription}
onChange={updateFormField("companyDescription")}
></TextField>
и функция, которая обновляет состояние моей компании всякий раз, когда меняются значения:
const updateFormField = (property: string) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => ({ ...prev, [property]: event.target.value }))
}
Это означает, что всякий раз, когда поле формы изменяется, я хотел бы создать новую копию (следовательно, оператор распространения) старого объекта.
Моя проблема в том, что в компании есть вложенные свойства, такие как company.location.address
:
{
name: "some company",
location: {
address: "some street"
}
// ...
}
Есть ли способ рефакторинга вышеуказанной функции для обновления вложенных свойств? Например, что-то вроде:
const updateFormField = (property: string[]) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => ({ ...prev, [...property]: event.target.value }))
}
Комментарии:
1. Вы имеете в виду, что хотите объединить старые/новые объекты для вашего
company[property]
?2. Я обновил свой ответ. Я каждый раз создаю новый объект (вот как работает React). Я бы хотел перезаписать определенные свойства, например
company.name
, илиcompany.location.address
.
Ответ №1:
Я не думаю, что для этого есть особенно простое решение, однако должна быть возможность выполнить циклический выбор/добавление нового пути:
const updateFormField = (property: string[]) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => {
// Take a copy of the state
const newObj = { ...prev }
// Set up a refernce to that object
let selected = newObj
// Loop through each property string in the array
for (let i = 0; i < property.length; i ) {
// If we're at the end of the properties, set the value
if (i === property.length - 1) {
selected[property[i]] = event.target.value
} else {
// If the property doesn't exist, or is a value we can't add a new property to, set it to a new object
if (typeof selected[property[i]] !== 'object') {
selected[property[i]] = {}
}
// Update our refernce to the currently selected property and repeat
selected = selected[property[i]]
}
}
// Return the object with each nested property added
return newObj
)}
}
Простой JS-рабочий пример того же метода:
const test = (prev, property, value) => {
const newObj = { ...prev
}
let selected = newObj
for (let i = 0; i < property.length; i ) {
if (i === property.length - 1) {
selected[property[i]] = value
} else {
if (typeof selected[property[i]] !== 'object') {
selected[property[i]] = {}
}
selected = selected[property[i]]
}
}
return newObj
}
console.log(test(
{"a": "1"},
["b", "c", "d"],
100
))
console.log(test(
{"a": "1"},
["a", "b"],
100
))
console.log(test(
{"a": {"b": {"c": 1}}},
["a", "b", "c"],
100
))
Комментарии:
1. Спасибо за ваши усилия. Я подозревал, что так оно и было. 🙁
2. Нет проблем, надеюсь, кто-то еще сможет ответить с помощью оператора/шаблона, о котором я не знаю, но, к сожалению, я с ним не сталкивался.
Ответ №2:
Object.assign() и динамический поиск внутренней ссылки должны это сделать. Я предполагаю, что тип ввода строки[] выше указывает, что вложенный путь представляет собой массив, такой как [«компания», «местоположение», «адрес»]:
const updateFormField = (property: string[]) => (event: ChangeEvent<HTMLInputElement>) => {
setCompany(prev => {
const copy = Object.assign({}, prev);
let ref = copy;
for (let i = 0; i < property.length - 1; i ) {
ref = ref[property[i]];
}
ref[property[property.length - 1]] = event.target.value
return copy;
});
}