Я создаю контролируемую форму с динамическими полями. Компонент Parent получает данные из хранилища избыточности, а затем устанавливает состояние со значениями. Я не хочу делать это со слишком большим количеством строк кода, поэтому я превращаю динамические поля в компонент. Состояния остаются в родительском компоненте, и я использую свойства для передачи функции смены дескриптора.
Родитель:
function EditAbout(props) {
const [img, setImg] = useState("");
const [body, setBody] = useState(props.about.body);
const [instagram, setInstagram] = useState(props.about.links.instagram);
const [linkedin, setLinkedIn] = useState(props.about.links.linkedin);
const [press, setPress] = useState(props.about.press)
const handleSubmit = (e) => {
// Submit the change to redux
};
// set states with redux store
useEffect(() => {
setBody(props.about.body);
setInstagram(props.about.links.instagram);
setLinkedIn(props.about.links.linkedin);
setPress(props.about.press);
}, []);
const handleChangeEventChild = (e, index) => {
e.preventDefault();
let articles = press
const {value, name } = e.target
if (name === "title") {
articles[index].title = value;
} else {
articles[index].link = value;
}
setPress(articles)
console.info(articles[index])
}
return (
<Box>
<h1>CHANGE ABOUT ME</h1>
<Input
label = "Image"
name = "img"
type = "file"
variant = "outlined"
margin = "normal"
onChange = {(e) => setImg(e.target.files)}
/>
<Input
label = "body"
value = {body}
name = "body"
onChange = {(e) => setBody(e.target.value)}
variant = "outlined"
multiline
rowsMax = {12}
margin = "normal"
/>
<Input
label = "instagram"
value = {instagram}
name = "instagram"
variant = "outlined"
margin = "normal"
onChange = {(e) => setInstagram(e.target.value)}
/>
<Input
label = "Linkedin"
value = {linkedin}
name = "linkedin"
variant = "outlined"
margin = "normal"
onChange = {(e) => setLinkedIn(e.target.value)}
/>
<Child press = {press} onChange = {handleChangeEventChild} />
{props.loading ? (
<CircularProgress color = "black" />
) : (
<Button onClick = {handleSubmit} variant = "contained">
Send
</Button>
)}
</Box>
);
}
Ребенок :
function Child(props) {
const { press, onChange } = props;
const inputsMarkup = () =>
press.map((article, index) => (
<div key = {`press${index}`} style = {{ display: "flex" }}>
<input
name = "title"
value = {press[index].title}
onChange = {(e) => onChange(e, index)}
/>
<input
name = "link"
value = {press[index].link}
onChange = {(e) => onChange(e, index)}
/>
<button>Delete</button>
</div>
));
return (
<div>
<h1>Press :</h1>
{inputsMarkup()}
</div>
);
}
Все в порядке, когда я печатаю родительские входы. Но когда я использую обновление состояния дочерних полей для одного символа, но сразу после этого возвращаюсь к предыдущему состоянию. Также не отображается смена персонажа. Я вижу это только в консоли. Заранее спасибо за помощь
Проблема в том, что вы изменяете состояние напрямую. Когда вы создаете переменную articles
(let articles = press
), вы на самом деле не создаете копию, а articles
фактически не содержит значения. Это всего лишь ссылка на это значение, которое указывает на расположение объекта в памяти.
Поэтому, когда вы обновляете articles[index].title
в своей handleChangeEventChild
функции, вы фактически также меняете состояние press
. Вы можете подумать, что это нормально, но без вызова setPress()
React не узнает об изменении. Итак, хотя значение состояния изменено, вы его не увидите, потому что React не будет его повторно отображать.
Вам нужно создать копию массива press
с помощью .map()
и создать копию обновленного элемента массива. Вы можете найти обновленный handleChangeEventChild()
ниже:
const handleChangeEventChild = (e, index) => {
e.preventDefault();
const { value, name } = e.target;
setPress(
// .map() returns a new array
press.map((item, i) => {
// if the current item is not the one we need to update, just return it
if (i !== index) {
return item;
}
// create a new object by copying the item
const updatedItem = {
...item,
};
// we can safely update the properties now it won't affect the state
if (name === 'title') {
updatedItem.title = value;
} else {
updatedItem.link = value;
}
return updatedItem;
}),
);
};
Большое спасибо за ваш ответ. Это сработало, и теперь я понимаю все, что я сделал неправильно.