使用 Child /Parent 组件反应受控表单
React Controlled Form with Child /Parent component
我正在构建一个带有动态字段的受控表单。
Parent 组件从 redux 存储中获取数据,然后使用这些值设置状态。
我不想让它有太多的代码行,所以我把动态字段变成了一个组件。
状态保留在 parent 组件中,我使用 props 传递 handlechange 函数。
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 handleChangeChild = (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.log(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={handleChangeChild} />
{props.loading ? (
<CircularProgress color="black" />
) : (
<Button onClick={handleSubmit} variant="contained">
Send
</Button>
)}
</Box>
);
}
Child :
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>
);
}
当我输入 Parent 输入时一切正常。但是,当我使用 Child 字段对一个字符进行状态更新时,但之后又返回到之前的状态。
它也不显示字符更改。我只能在控制台中看到它。
预先感谢您的帮助
问题是您正在直接改变状态。当您创建 articles
变量 (let articles = press
) 时,您实际上并没有创建一个副本,并且 articles
实际上并不包含该值。它只是对该值的引用,它指向对象在内存中的位置。
因此,当您在 handleChangeChild
函数中更新 articles[index].title
时,您实际上也在更改 press
状态。您可能认为这很好,但如果不调用 setPress()
,React 将不会意识到更改。所以,虽然状态值改变了,但你不会看到它,因为 React 不会重新渲染它。
您需要使用 .map()
创建 press
数组的副本,并创建更新后的数组元素的副本。您可以在下面找到更新后的 handleChangeChild()
:
const handleChangeChild = (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;
}),
);
};
我正在构建一个带有动态字段的受控表单。 Parent 组件从 redux 存储中获取数据,然后使用这些值设置状态。 我不想让它有太多的代码行,所以我把动态字段变成了一个组件。 状态保留在 parent 组件中,我使用 props 传递 handlechange 函数。
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 handleChangeChild = (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.log(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={handleChangeChild} />
{props.loading ? (
<CircularProgress color="black" />
) : (
<Button onClick={handleSubmit} variant="contained">
Send
</Button>
)}
</Box>
);
}
Child :
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>
);
}
当我输入 Parent 输入时一切正常。但是,当我使用 Child 字段对一个字符进行状态更新时,但之后又返回到之前的状态。 它也不显示字符更改。我只能在控制台中看到它。 预先感谢您的帮助
问题是您正在直接改变状态。当您创建 articles
变量 (let articles = press
) 时,您实际上并没有创建一个副本,并且 articles
实际上并不包含该值。它只是对该值的引用,它指向对象在内存中的位置。
因此,当您在 handleChangeChild
函数中更新 articles[index].title
时,您实际上也在更改 press
状态。您可能认为这很好,但如果不调用 setPress()
,React 将不会意识到更改。所以,虽然状态值改变了,但你不会看到它,因为 React 不会重新渲染它。
您需要使用 .map()
创建 press
数组的副本,并创建更新后的数组元素的副本。您可以在下面找到更新后的 handleChangeChild()
:
const handleChangeChild = (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;
}),
);
};