首页 > 解决方案 > 反应插入/删除数组中的行问题未更新

问题描述

需要建议。我有一个基本的父 (AddressList) 和子 (Address) 组件层次结构。添加删除按钮位于地址组件中。

我的问题是,当用户选择下拉菜单和文本字段时,值会更新,但是如何更新 Parent 组件中的数组?当我按 + 按钮添加新行时,数组不会更新显示填充字段的行。所有行都返回到它们的初始状态。

我尝试使用 useReducer() 但我得到了相同的结果。

想法?

const AddressList = (props) => {
  const [addresses, setAddresses] = React.useState(props.addresses);

  const handleAddRow = () => {
    let rows = [...addresses];
    rows.push(AddressModel);
    setAddresses(rows);
  };

  const handleRemoveRow = (index) => {
    let rows = [...addresses];
    rows.splice(index, 1);
    setAddresses(rows);
  };

  return (
    <React.Fragment>
      { addresses.map((address, index) =>
        <Address
          index={index}
          item={address}
          key={Math.random()}
          handleAddRow={handleAddRow}
          handleRemoveRow={handleRemoveRow}/>
      )}
    </React.Fragment>
  );
};

export default AddressList;

const useStyles = makeStyles(theme => ({
  button: {
    margin: theme.spacing(1),
  },
  root: {
    display: 'inline-flex',
    maxWidth: 800,
    minWidth: 300,
    padding: 10,
    width: '100%'
  }
}));

const types = ['Home', 'Work'];

const Address = (props) => {
  const classes = useStyles();
  // const [address, setAddress] = React.useState(props.item);
  const [address, setAddress] = useReducer((myArray, { type, value }) => {
    switch (type) {
      case "add":
        return [...myArray, value];
      case "remove":
        return myArray.filter((_, index) => index !== value);
      default:
        return myArray;
    }
  }, [address]);

  const handleAddressChange = name => event => {
    setAddress({ ...address, [name]: event.target.value });
  };

  const index = props.index;
  const handleAddRow = props.handleAddRow;
  const handleRemoveRow = props.handleRemoveRow;

  return (
    <Grid container className={classes.root} spacing={3}>
      <Grid item xs={12} sm={2}>
        <TextField
          id="type"
          label="Email Type"
          margin="normal"
          value={address.type}
          onChange={handleAddressChange('type')}
          fullWidth
          select>
          {types.map(option => (
            <MenuItem key={option} value={option}>{option}</MenuItem>
          ))}
        </TextField>
      </Grid>
      <Grid item xs={12} sm={9}>
        <TextField
          id="address"
          label="Address"
          margin="normal"
          value={address.address}
          onChange={handleAddressChange('address')}
          fullWidth
          required/>
      </Grid>
      <Grid item xs={12} sm={1}>
        {
          index === 0 ?
            <IconButton className={classes.button} aria-label="add" onClick={() => handleAddRow()}>
              <AddIcon/>
            </IconButton>
          :
            <IconButton className={classes.button} aria-label="delete" onClick={() => handleRemoveRow(index)}>
              <DeleteIcon/>
            </IconButton>
        }
      </Grid>
    </Grid>
  );
};

export default Address;

export const AddressModel = {
    id: 0,
    type: 'Home',
    address: ''
};

标签: arraysreactjsreact-hooks

解决方案


我在这里的代码和框中创建了一个工作模型

主要更改是首先更新您的地址模型,以便每个项目都有一个id而不是依赖于索引。

const addresses = [
  { id: 0, address: "home address 1", type: "Home" },
  { id: 1, address: "home address 2", type: "Home" },
  { id: 2, address: "work address 1", type: "Work" }
];

...然后将您的地址的更新代码移动到父级。所以你只在一个地方拥有你的状态,你在那里处理所有的更新。

所以你AddressList现在看起来像这样:

const AddressList = props => {
  const [addresses, setAddresses] = React.useState(props.addresses);

  const handleAddRow = () => {
    setAddresses([
      ...addresses,
      { id: addresses.length, address: "new address", type: "Home" }
    ]);
  };

  const handleRemoveRow = id => {
    const rows = addresses.filter(address => address.id !== id);
    setAddresses(rows);
  };
  const updateAddress = (id, ev) => {
    const rows = addresses.map(item =>
      item.id === id ? { ...item, address: ev.target.value } : item
    );
    setAddresses(rows);
  };
  const updateType = (id, ev) => {
    const rows = addresses.map(item =>
      item.id === id ? { ...item, type: ev.target.value } : item
    );
    setAddresses(rows);
  };
  return (
    <React.Fragment>
      {addresses.map((address, index) => (
        <Address
          index={index}
          item={address}
          key={address.id}
          handleAddRow={handleAddRow}
          handleRemoveRow={handleRemoveRow}
          updateType={updateType}
          updateAddress={updateAddress}
        />
      ))}
    </React.Fragment>
  );
};

还有你的地址:

const Address = props => {
  const {
    updateType,
    updateAddress,
    item,
    index,
    handleAddRow,
    handleRemoveRow
  } = props;
  return (
    <Grid container spacing={3}>
      <Grid item xs={12} sm={2}>
        <TextField
          id="type"
          label="Email Type"
          margin="normal"
          value={item.type}
          onChange={ev => updateType(item.id, ev)}
          fullWidth
          select
        >
          {types.map(option => (
            <MenuItem key={option} value={option}>
              {option}
            </MenuItem>
          ))}
        </TextField>
      </Grid>
      <Grid item xs={12} sm={9}>
        <TextField
          id="address"
          label="Address"
          margin="normal"
          value={item.address}
          onChange={ev => updateAddress(item.id, ev)}
          fullWidth
          required
        />
      </Grid>
      <Grid item xs={12} sm={1}>
        {index === 0 ? (
          <IconButton aria-label="add" onClick={handleAddRow}>
            Add
          </IconButton>
        ) : (
          <IconButton
            aria-label="delete"
            onClick={() => handleRemoveRow(item.id)}
          >
            Remove
          </IconButton>
        )}
      </Grid>
    </Grid>
  );
};

推荐阅读