используя React, я смог обновить значение объекта, хранящегося в родительском компоненте, из onChange()
, созданного в дочернем компоненте.
Однако я пытаюсь сделать то же самое с компонентом, созданным из списка сопоставленных объектов, поэтому он обновляет только значение в списке объектов, который создал этот компонент. У меня ничего не получается, но вот пример того, что я делаю:
Родительский компонент:
const Parent = () => {
const [banners, bannersUpdate] = React.useState([
{
id: 1, name: "banner1", nameSize: 14
},
{
id: 2, name: "banner2", nameSize: 16
},
{
id: 3, name: "banner3", nameSize: 18
}
]);
const handleUpdateNameSize = (id, e) => {
banners.nameSize = e.value
bannersUpdate({
nameSize: banners.nameSize,
...banners
})
}
return(
<section className = "banners">
{banners.map(banner =>
<Banner
key = {banner.id}
id = {banner.id}
name = {banner.name}
nameSize = {banner.nameSize}
updateNameSize = {handleUpdateNameSize}
/>
)}
</section>
);
}
Дочерний компонент:
const Banner = (props) => {
return (
<div className = "banCont">
<h2 style = {{fontSize: props.nameSize + 'px'}}>{props.name}</h2>
<input onChange = {(e) => props.updateNameSize(props.id, e.target)} className = {'slider1-' + props.name} />
</div>
)
}
Учитывая вышеизложенное, он не обновляет элемент объекта, относящийся к этому баннеру, а просто добавляет новый элемент объекта на верхний уровень с этим именем и значением.
Я считаю, что ответ где-то зависит от использования key
, и я пробовал заменить все banners
на banners[id]
, например: banners[id].nameSize = e.value
, но по какой-то причине обновление значения не работает.
Помощь будет оценена, пожалуйста, спасибо.
🤔 А знаете ли вы, что...
JavaScript используется для разработки современных одностраничных приложений (SPA), где весь контент загружается асинхронно.
const Parent = () => {
const [banners, setBanners] = React.useState([
{
id: 1, name: "banner1", nameSize: 14
},
{
id: 2, name: "banner2", nameSize: 16
},
{
id: 3, name: "banner3", nameSize: 18
}
]);
const handleUpdateNameSize = (id, value) => {
setBanners(prevBanners =>
prevBanners.map(banner =>
banner.id === id ? { ...banner, nameSize: value } : banner
)
);
};
return (
<section className = "banners">
{banners.map(banner => (
<Banner
key = {banner.id}
id = {banner.id}
name = {banner.name}
nameSize = {banner.nameSize}
updateNameSize = {handleUpdateNameSize}
/>
))}
</section>
);
};
const Banner = (props) => {
return (
<div className = "banCont">
<h2 style = {{ fontSize: props.nameSize + 'px' }}>{props.name}</h2>
<input
type = "range"
min = "10"
max = "50"
value = {props.nameSize}
onChange = {(e) => props.updateNameSize(props.id, e.target.value)}
className = {'slider1-' + props.name}
/>
</div>
);
};
Проблема заключается в неправильном подходе к обновлению состояния вложенного объекта внутри массива в React. Первоначальная попытка включала непосредственное изменение массива (баннеров), а затем попытку обновления состояния с неполной и неправильной структурой, что приводило к несогласованности состояния и неожиданному поведению. В React обновления состояния должны выполняться неизменно, чтобы обеспечить надлежащую реактивность и рендеринг. Это означает создание нового массива, включающего обновленный объект, а не непосредственное изменение существующего массива.
Подробнее об этом можно прочитать здесь https://reacttraining.com/blog/state-in-react-is-immutable
у вашей функции обновления есть некоторые проблемы. Сначала banners.nameSize = e.value
вы пытаетесь добавить nameSize
«поле» в «массив» своих баннеров. Вместо этого вам следует найти соответствующий баннер с помощью id
и обновить его значение nameSize
.
Также, как упоминается в другом ответе. Используйте map
, чтобы убедиться, что состояние изменено и компонент повторно визуализируется. Использование find
по идентификатору и последующее обновление поля nameSize приведет к изменению состояния и не приведет к повторному рендерингу.
Также лучше следовать стандартам именования для функции обновления useState. если состояние banners
, то имя функции типа setBanners
const Banner = ({id, name, nameSize, updateNameSize}) => {
return (
<div className = "banCont">
<h2 style = {{fontSize: nameSize + 'px'}}>{name}</h2>
<input onChange = {(e) => updateNameSize(id, e.target)} className = {'slider1-' + name} />
</div>
)
}
const Parent = () => {
const [banners, setBanners] = React.useState([
{ id: 1, name: "banner1", nameSize: 14 },
{ id: 2, name: "banner2", nameSize: 16 },
{ id: 3, name: "banner3", nameSize: 18 }
]);
const handleUpdateNameSize = (id, e) => {
const nameSize = e.value.length;
setBanners(prevBanner => prevBanner.map(banner => banner.id === id ? { ...banner, nameSize } : banner))
}
return(
<div style = {{display:'flex'}}>
<section className = "banners">
{banners.map(banner =>
<Banner
key = {banner.id}
id = {banner.id}
name = {banner.name}
nameSize = {banner.nameSize}
updateNameSize = {handleUpdateNameSize}
/>
)}
</section>
<pre>{JSON.stringify(banners, null, 2)}</pre>
</ div>
);
};
const App = () => {
return (
<Parent />
);
};
ReactDOM.render(<App />, document.getElementById("app"));
<script crossorigin src = "https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src = "https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div id = "app"></div>