React,组件在数组状态更改后不重新渲染
React, component not re-rendering after change in an array state
删除状态中的元素后,组件没有重新呈现,但状态确实发生了变化。
在组件中,您可以通过表单在数组(这是一个状态)中添加一个元素,查看数组中的所有元素,并使用按钮将其从状态中删除。因此,在删除处于状态的元素后,组件不会重新呈现。以下是组件的代码:
import React, { useEffect, useState } from 'react';
import {
Typography,
IconButton,
Button,
TextField,
Paper,
} from '@mui/material';
import {
CancelOutlined,
AddBoxOutlined,
VisibilityOutlined,
VisibilityOffOutlined,
} from '@mui/icons-material';
export default function Test1() {
const [subNames, setSubNames] = useState([]);
const [subName, setSubName] = useState('');
const [showSubForm, setShowSubForm] = useState(false);
const onSubNameChange = (e) => {
setSubName(e.target.value);
};
const onSubNameSubmit = () => {
if (!subName) return alert('Enter name!');
setSubNames((prev) => prev.concat({ name: subName }));
setShowSubForm(false);
setSubName('');
};
const subForm = (
<>
<div
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<TextField
label='Sub Todo Name'
onChange={onSubNameChange}
name='subTodoName'
value={subName}
size='small'
variant='outlined'
fullWidth
/>
<IconButton onClick={onSubNameSubmit}>
<AddBoxOutlined color='primary' />
</IconButton>
</div>
<br />
</>
);
const onDelete = (position, e) => {
let arr = subNames;
arr.splice(position, 1);
setSubNames(arr);
};
return (
<div>
<h1>Hello World!</h1>
{subNames.map((item, key) => (
<Paper
key={key}
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
margin: 'auto',
padding: 10,
marginTop: 10,
borderRadius: '10px',
}}
elevation={3}>
<div sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant='body1'>
<b>Sub Todo-{key + 1}:</b>
</Typography>
<Typography variant='body1'>{item?.name}</Typography>
</div>
<IconButton onClick={(e) => onDelete(key, e)}>
<CancelOutlined color='primary' />
</IconButton>
</Paper>
))}
<br />
{showSubForm && subForm}
<div>
{showSubForm && (
<Button
variant='contained'
sx={{ float: 'right' }}
color='primary'
size='small'
startIcon={<VisibilityOffOutlined />}
onClick={() => setShowSubForm(false)}>
Add sub todo item
</Button>
)}
{!showSubForm && (
<Button
variant='contained'
sx={{ float: 'right' }}
onClick={() => setShowSubForm(true)}
color='primary'
size='small'
startIcon={<VisibilityOutlined />}>
Add sub todo item
</Button>
)}
</div>
</div>
);
}
React 不会 re-render 因为它好像什么都没有改变,也就是说每次你给 setState
相同的 state
。对于像 Number
、String
和 Boolean
这样的原始值,很明显知道我们是否给出了不同的值。另一方面,对于 Object
和 Array
等引用值,更改它们的 content
不会将它们标记为不同。应该是不同的 reference
.
正如您所做的那样,您正在为 setSubNames
提供相同的内存引用。查看您的注释代码以了解您做错了什么:
let arr = subNames; // will do a reference copy => arr == subNames
arr.splice(position, 1); // will change its content => arr == subNames
setSubNames(arr); // at this point it's like nothing has changed
一个解决方案可能是 spread operator,将创建现有数组的副本,但在新的内存引用上,如下所示:
// changes the content of the array while keeping its memory reference
subNames.splice(position, 1);
// the spread operator creates a copy on a new memory reference
setSubNames([...subNames]);
删除状态中的元素后,组件没有重新呈现,但状态确实发生了变化。 在组件中,您可以通过表单在数组(这是一个状态)中添加一个元素,查看数组中的所有元素,并使用按钮将其从状态中删除。因此,在删除处于状态的元素后,组件不会重新呈现。以下是组件的代码:
import React, { useEffect, useState } from 'react';
import {
Typography,
IconButton,
Button,
TextField,
Paper,
} from '@mui/material';
import {
CancelOutlined,
AddBoxOutlined,
VisibilityOutlined,
VisibilityOffOutlined,
} from '@mui/icons-material';
export default function Test1() {
const [subNames, setSubNames] = useState([]);
const [subName, setSubName] = useState('');
const [showSubForm, setShowSubForm] = useState(false);
const onSubNameChange = (e) => {
setSubName(e.target.value);
};
const onSubNameSubmit = () => {
if (!subName) return alert('Enter name!');
setSubNames((prev) => prev.concat({ name: subName }));
setShowSubForm(false);
setSubName('');
};
const subForm = (
<>
<div
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<TextField
label='Sub Todo Name'
onChange={onSubNameChange}
name='subTodoName'
value={subName}
size='small'
variant='outlined'
fullWidth
/>
<IconButton onClick={onSubNameSubmit}>
<AddBoxOutlined color='primary' />
</IconButton>
</div>
<br />
</>
);
const onDelete = (position, e) => {
let arr = subNames;
arr.splice(position, 1);
setSubNames(arr);
};
return (
<div>
<h1>Hello World!</h1>
{subNames.map((item, key) => (
<Paper
key={key}
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
margin: 'auto',
padding: 10,
marginTop: 10,
borderRadius: '10px',
}}
elevation={3}>
<div sx={{ display: 'flex', alignItems: 'center' }}>
<Typography variant='body1'>
<b>Sub Todo-{key + 1}:</b>
</Typography>
<Typography variant='body1'>{item?.name}</Typography>
</div>
<IconButton onClick={(e) => onDelete(key, e)}>
<CancelOutlined color='primary' />
</IconButton>
</Paper>
))}
<br />
{showSubForm && subForm}
<div>
{showSubForm && (
<Button
variant='contained'
sx={{ float: 'right' }}
color='primary'
size='small'
startIcon={<VisibilityOffOutlined />}
onClick={() => setShowSubForm(false)}>
Add sub todo item
</Button>
)}
{!showSubForm && (
<Button
variant='contained'
sx={{ float: 'right' }}
onClick={() => setShowSubForm(true)}
color='primary'
size='small'
startIcon={<VisibilityOutlined />}>
Add sub todo item
</Button>
)}
</div>
</div>
);
}
React 不会 re-render 因为它好像什么都没有改变,也就是说每次你给 setState
相同的 state
。对于像 Number
、String
和 Boolean
这样的原始值,很明显知道我们是否给出了不同的值。另一方面,对于 Object
和 Array
等引用值,更改它们的 content
不会将它们标记为不同。应该是不同的 reference
.
正如您所做的那样,您正在为 setSubNames
提供相同的内存引用。查看您的注释代码以了解您做错了什么:
let arr = subNames; // will do a reference copy => arr == subNames
arr.splice(position, 1); // will change its content => arr == subNames
setSubNames(arr); // at this point it's like nothing has changed
一个解决方案可能是 spread operator,将创建现有数组的副本,但在新的内存引用上,如下所示:
// changes the content of the array while keeping its memory reference
subNames.splice(position, 1);
// the spread operator creates a copy on a new memory reference
setSubNames([...subNames]);