更新 redux 存储中的对象数组

问题描述 投票:0回答:1

以下是我的组件,它触发操作并监视更改:

import { Box, Button, Stack, TextField, Typography } from "@mui/material";
import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { v4 as uuidv4 } from "uuid";
import {
  handleDirectorAdd,
  handleDirectorInfoFormChange,
  handleDirectorRemove,
} from "../../../../redux/slices/directorInfo";

const DirectorsInformation = () => {
  const dispatch = useDispatch();
  const directorsInfo = useSelector((state) => state.directorInfo);

  const handleChange = (e, index) => {
    const { name, value } = e.target;
    if (name && value) {
      dispatch(handleDirectorInfoFormChange({ name, value, index }));
    }
  };

  const handleAdd = () => {
    dispatch(handleDirectorAdd());
  };

  const handleRemove = (e, index) => {
    dispatch(handleDirectorRemove({ index }));
  };

  return (
    <form
      onSubmit={handleSubmit}
      noValidate
      style={{
        padding: "1rem",
        backgroundColor: "white",
        border: "1px solid darkgrey",
        textAlign: "center",
      }}
    >
      <Typography variant="body1">
        <strong>Director&apos;s Information</strong>
      </Typography>
      <hr />
      {directorsInfo?.map((directorInfo, index) => (
        <div key={uuidv4()}>
          <Stack
            spacing={1}
            alignItems="center"
            style={{
              padding: "1rem",
              backgroundColor: "rgb(227, 227, 227)",
            }}
          >
            <Typography variant="body1">Director&apos;s Information</Typography>
            <Button
              variant="contained"
              color="error"
              sx={{ textTransform: "none" }}
              onClick={(e) => handleRemove(e, index)}
            >
              Remove
            </Button>
            <Typography variant="body1">
              <strong>Full Name</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Full Name"
              name="name"
              value={directorInfo.name}
              onChange={(e) => handleChange(e, index)}
              sx={{ backgroundColor: "white" }}
            />
            <Typography variant="body1">
              <strong>Official Email</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Official Email"
              name="email"
              type="email"
              value={directorInfo.email}
              onChange={(e) => handleChange(e, index)}
              sx={{ backgroundColor: "white" }}
            />
            <Typography variant="body1">
              <strong>Contact Number</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Contact Number"
              name="contactNumber"
              value={directorInfo.contactNumber}
              onChange={(e) => handleChange(e, index)}
              sx={{ backgroundColor: "white" }}
            />
            <Typography variant="body1">
              <strong>Title</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Tile"
              name="title"
              type="string"
              value={directorInfo.title}
              onChange={(e) => handleChange(e, index)}
              sx={{ backgroundColor: "white" }}
            />
          </Stack>
          <Button
            variant="contained"
            color="success"
            sx={{ textTransform: "none", margin: "2rem 0" }}
            onClick={handleAdd}
          >
            Add Directors Information
          </Button>
        </div>
      ))}
    </form>
  );
};

export default DirectorsInformation;

现减速机及动作如下:

import { createSlice, current } from "@reduxjs/toolkit";
const initialValues = {
  name: "",
  email: "",
  contactNumber: "",
  username: "",
  title: "",
};
const initialState = [initialValues];

const directorInfo = createSlice({
  name: "directorInfo",
  initialState,
  reducers: {
    handleDirectorAdd(state, action) {
      return [...state, initialValues];
    },
    handleDirectorRemove(state, action) {
      return state.filter((_, index) => index !== action.payload.index);
    },
    handleDirectorInfoFormChange(state, action) {
      const { index, name, value } = action.payload;
      return state.map((director, idx) => {
          if (idx === index) {
          return {
            ...director,
            [name]: value,
          };
        } else {
          return director;
        }
      });
    },
  },
});

export const {
  handleDirectorAdd,
  handleDirectorRemove,
  handleDirectorInfoFormChange,
} = directorInfo.actions;

export default directorInfo.reducer;

我面临的问题是,当我更改表单输入字段中的值时,该值会更新,但会导致重新渲染(我假设因为输入字段的焦点已被删除)。当任何控制器的值发生更改或添加和删除它们时,我试图更新 redux 存储中的状态。任何帮助将非常感激。

谢谢你

我试图改变状态。状态正在正确改变,但它导致重新渲染,这是我不想要的。我尝试过过滤功能而不是地图,但仍然无法达到预期的结果。我希望用户能够顺利地在输入框内书写

javascript reactjs redux react-redux redux-toolkit
1个回答
0
投票

问题

当您更新状态时,重新渲染将是预期的结果。您遇到的根本问题是由于在映射的

directorsInfo
状态数组上的每个渲染周期使用随机 React 键引起的。

{directorsInfo?.map((directorInfo, index) => (
  <div key={uuidv4()}> // <-- Random React key == bad
    ...
  </div>
))}

每次

DirectorsInformation
重新渲染时,每个映射的
directorsInfo
数组元素的 JSX 都使用一个 不同 React 键(与上一个渲染周期相比),因此 React 将此 JSX 视为新的并卸载旧的JSX 并安装新的 JSX 即使它是相同的 UI。

解决方案

几乎不要使用随机的 React 键。理想情况下,您希望使用与您正在呈现的数据本质上相关的键值,例如在数据生命周期中与数据一起存在的任何 GUID 或数据的唯一属性。

在这种情况下,我建议向您的州添加一个

id

 属性。如果您愿意,可以继续使用 
uuid
 包,但 Redux-Toolkit 还可以针对此类用例重新导出 
nanoid

示例:

import { createSlice, nanoid } from "@reduxjs/toolkit"; const initialValues = { name: "", email: "", contactNumber: "", username: "", title: "", }; const initialState = [{ ...initialValues, id: nanoid(), // <-- generate id when creating object }]; const directorInfo = createSlice({ name: "directorInfo", initialState, reducers: { handleDirectorAdd(state, action) { state.push({ ...initialValues, id: nanoid(), // <-- generate id when creating object }); }, handleDirectorRemove(state, action) { return state.filter((director) => director.id !== action.payload.id); }, handleDirectorInfoFormChange(state, action) { const { id, name, value } = action.payload; return state.map((director) => director.id === id ? { ...director, [name]: value } : director); }, }, });
const DirectorsInformation = () => {
  const dispatch = useDispatch();
  const directorsInfo = useSelector((state) => state.directorInfo);

  const handleChange = (e, id) => {
    const { name, value } = e.target;
    if (name && value) {
      dispatch(handleDirectorInfoFormChange({ name, value, id }));
    }
  };

  const handleAdd = () => {
    dispatch(handleDirectorAdd());
  };

  const handleRemove = (e, id) => {
    dispatch(handleDirectorRemove({ id }));
  };

  return (
    <form
      onSubmit={handleSubmit}
      noValidate
      style={{
        padding: "1rem",
        backgroundColor: "white",
        border: "1px solid darkgrey",
        textAlign: "center",
      }}
    >
      <Typography variant="body1">
        <strong>Director&apos;s Information</strong>
      </Typography>
      <hr />
      {directorsInfo?.map((director) => (
        <div key={director.id}> // <-- use id property
          <Stack
            spacing={1}
            alignItems="center"
            style={{
              padding: "1rem",
              backgroundColor: "rgb(227, 227, 227)",
            }}
          >
            <Typography variant="body1">Director&apos;s Information</Typography>
            <Button
              variant="contained"
              color="error"
              sx={{ textTransform: "none" }}
              onClick={(e) => handleRemove(e, director.id)} // <-- pass id property
            >
              Remove
            </Button>
            <Typography variant="body1">
              <strong>Full Name</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Full Name"
              name="name"
              value={director.name}
              onChange={(e) => handleChange(e, director.id)} // <-- pass id property
              sx={{ backgroundColor: "white" }}
            />
            <Typography variant="body1">
              <strong>Official Email</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Official Email"
              name="email"
              type="email"
              value={director.email}
              onChange={(e) => handleChange(e, director.id)} // <-- pass id property
              sx={{ backgroundColor: "white" }}
            />
            <Typography variant="body1">
              <strong>Contact Number</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Contact Number"
              name="contactNumber"
              value={director.contactNumber}
              onChange={(e) => handleChange(e, director.id)} // <-- pass id property
              sx={{ backgroundColor: "white" }}
            />
            <Typography variant="body1">
              <strong>Title</strong>
            </Typography>
            <TextField
              fullWidth
              size="small"
              label="Tile"
              name="title"
              type="string"
              value={director.title}
              onChange={(e) => handleChange(e, director.id)} // <-- pass id property
              sx={{ backgroundColor: "white" }}
            />
          </Stack>
          <Button
            variant="contained"
            color="success"
            sx={{ textTransform: "none", margin: "2rem 0" }}
            onClick={handleAdd}
          >
            Add Directors Information
          </Button>
        </div>
      ))}
    </form>
  );
};
    
© www.soinside.com 2019 - 2024. All rights reserved.