简体   繁体   中英

React, component not re-rendering after change in an array state

The component is not rerendering after the deletion of an element in the state but the state does change. In the component, you can add an element in the array (which is a state) through form, see all the elements in the array, and delete it from the state using the button. So after deleting an element that is in the state, the component does not rerender. Following is the code of the component:

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>
  );
}

React won't re-render because it's like nothing has changed, that is every time you give the same state to a setState . For primitive values like Number , String , and Boolean , it's obvious to know wether we are giving a different value or not.

For referenced values like Object and Array in the other hand, changing their content doesn't flag them as different. It should be a different memory reference . See your commented code to understand what you are doing wrong:

let arr = subNames;      // does a reference copy => arr == subNames
arr.splice(position, 1); // changes its content => arr == subNames
setSubNames(arr);        // at this point it's like nothing has changed

A solution could be the spread operator , will create a copy of your existing array but on a new memory reference , like so:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM