React 中数组状态更改后组件未更新/重新渲染

Dan*_*lik 16 javascript reactjs

删除状态中的元素后,组件不会重新渲染,但状态确实会发生变化。在组件中,您可以通过表单在数组(这是一个状态)中添加一个元素,查看数组中的所有元素,并使用按钮将其从状态中删除。因此,删除处于该状态的元素后,该组件不会重新渲染。以下是该组件的代码:

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>
            &nbsp;
            <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>
  );
}
Run Code Online (Sandbox Code Playgroud)

You*_*mar 35

每次你statesetState. Number对于诸如、String和 之类的原始值Boolean,很明显可以知道我们是否给出了不同的值。

另一方面,对于像Object和那样的引用值,更改它们并不会将它们标记为不同。应该是不一样的记忆。查看您的注释代码以了解您做错了什么:Arraycontentreference

let arr = subNames;      // does a reference copy, means arr === subNames
arr.splice(position, 1); // changes its content, but still arr === subNames
setSubNames(arr);        // at this point it's like nothing has changed
Run Code Online (Sandbox Code Playgroud)

一个解决方案可能是扩展运算符,它将创建现有数组的副本,但在新内存上reference,如下所示:

let arr = [...subNames]; // creates a copy of subNames on a new reference
arr.splice(position, 1); // updates the content of the newly created array 
setSubNames(arr);        // new reference is given to setSubNames
Run Code Online (Sandbox Code Playgroud)