代码之家  ›  专栏  ›  技术社区  ›  Clebo Sevic

React:从回调访问State内部的数组时未定义

  •  0
  • Clebo Sevic  · 技术社区  · 1 年前

    我有一个回调函数,它应该编辑State内部的数组中的值。当我尝试访问 dataZähler ,所以数组本身,我得到了未定义的/长度为0的数组。为什么?

    有问题的代码(希望简化为最基本的代码):

    'use client'
    import { useCallback, useEffect, useState } from "react";
    import { ProtokollEintrag, TogglePosition, ZählerCallback, ZählerObjekt } from "./dataTypes";
    import { ToggleButton, ToggleButtonGroup, Divider, ZoomProps } from "@mui/material";
    import ElectricalServicesIcon from '@mui/icons-material/ElectricalServices';
    import clsx from "clsx";
    import { Modal } from "react-bootstrap";
    import { CreateCounterPopUp, CreateEntryPopUp, EditCounterPopUp, EditEntryPopUp, CalculateDeltaPopUp } from "./pages/myPopups";
    
    export default function Home() {
      //data and internal logic states, table-content, which rows are selected, is the data still loading, etc.
      const [loading, setLoading] = useState(true);
      const [dataZähler, setDataZähler] = useState<ZählerObjekt[]>([]);
      const [dataProtokoll, setDataProtokoll] = useState<ProtokollEintrag[]>([]);
      const [selectedRowZähler, setSelectedRowZähler] = useState(-1);
      const [selectedRowProtokoll, setSelectedRowProtokoll] = useState(-1);
      //View-Logic states, which table is shown, if modals should be seen, etc
      const [showEditZählerModal, setShowEditZählerModal] = useState(false);
    
      //on start, load both csv's into states
      useEffect(() => {
        fetch('/api/loadCsv')
          .then((res) => res.json())
          .then((data) => {
            setDataZähler(data.dataZähler)
            setDataProtokoll(data.DataProtokoll)
            setLoading(false)
          })
      }, []);
    
      function handleEditZähler() {
        if (currentTable === TogglePosition.Zähler && selectedRowZähler >= 0) {
          setShowEditZählerModal(true);
        }
      };
    
      const handleEditZählerCallback: ZählerCallback = useCallback((data: ZählerObjekt | null) => {
        console.log(dataZähler);
        if (data) {
          const oldData = dataZähler.map((elem, idx) => {
            if (idx === selectedRowZähler)
              return { ...data };
            return elem;
          });
          console.log(data);
          console.log(oldData);
          setDataZähler(oldData);
          setSelectedRowZähler(-1);
        }
        setShowEditZählerModal(false);
      }, []);
    
      return (
        <div className="flex flex-col">
          {/* Buttons und Toggle zur Verwendung und Steuerung des Interfaces */}
          <div className="justify-center flex sticky bottom-0 space-x-4 bg-[white] bg-opacity-60">
          <button onClick={handleEditZähler} disabled={selectedRowZähler < 0} className={clsx("bg-[#0ea5e9] border-[none] text-[white] text-center inline-block text-base rounded-[5px] px-5 py-3 my-2", { "bg-[#d1d5db]": selectedRowZähler < 0 })}>Bearbeiten</button>
          </div>
          {/*Modal Div - all edit/Cancel/Save/Create/Delete modals go here*/}
          <div>
            <Modal size="lg" show={showEditZählerModal} onHide={() => setShowEditZählerModal(false)}>
              <Modal.Header closeButton>
                <Modal.Title>
                  Zähler Bearbeiten:
                </Modal.Title>
              </Modal.Header>
              <Modal.Body>
                <EditCounterPopUp data={dataZähler[selectedRowZähler]} callback={handleEditZählerCallback} />
              </Modal.Body>
            </Modal>
          </div>
        </div>
      );
    }
    

    如果很重要,这里是编辑Z hlerObjekt的模式:

    import { ChangeEvent, FormEvent, useState } from "react";
    import { ZählerObjekt, ProtokollEintrag, ZählerCallback, EintragCallback } from "../dataTypes";
    import { Form } from "react-bootstrap";
    import { generateGenericObjectID } from "../util/objectIdentifier";
    
    interface CounterProps {
        data: ZählerObjekt,
        callback: ZählerCallback
    }
    
    export function EditCounterPopUp({ data, callback }: CounterProps) {
        const [newCounter, setNewCounter] = useState({ ...data });
    
        function handleSpeichern(event: React.MouseEvent<HTMLButtonElement>) { //dont forget to set the id of the new object
            event.preventDefault();
            setNewCounter({ ...newCounter, ...{ id: generateGenericObjectID(newCounter) } });
            callback(newCounter);
        }
    
        function handleInputChange(field: string, event: ChangeEvent<HTMLInputElement>) {
            const newState = { ...newCounter };
            newState[field as keyof ZählerObjekt] = event.target.value;
            setNewCounter(newState);
        }
    
        return (
            <div>
                <form>
                    {Object.entries(newCounter).slice(1).map((elem, idx) => {
                        return (
                            <Form.Group
                                className="mb-3" key={idx} controlId={elem[0]}
                            >
                                <Form.Label>{elem[0]}</Form.Label>
                                <Form.Control
                                    as="textarea"
                                    rows={2}
                                    placeholder={"z.B. " + String(data[elem[0] as keyof ZählerObjekt])}
                                    value={elem[1]}
                                    onChange={(e) => handleInputChange(elem[0], e as any)} />
                            </Form.Group>
                        );
                    })}
                    <div className="float-right">
                        <button onClick={handleSpeichern} className="bg-[#22c55e] border-[none] text-[white] text-center inline-block text-base rounded-[5px] px-5 py-3 my-2">Speichern</button>
                    </div>
                </form>
            </div>
        );
    }
    

    为什么 dataZ处理程序 在回调中未定义?我该怎么解决这个问题?

    1 回复  |  直到 1 年前
        1
  •  1
  •   Simran Singh    1 年前

    问题是一个空的依赖项数组。包括 dataZähler selectedRowZähler 的依赖数组中 useCallback 。检查以下代码:

    const handleEditZählerCallback: ZählerCallback = useCallback((data: ZählerObjekt | null) => {
      if (data) {
        const oldData = dataZähler.map((elem, idx) => {
          if (idx === selectedRowZähler)
            return { ...data };
          return elem;
        });
        setDataZähler(oldData);
        setSelectedRowZähler(-1);
      }
      setShowEditZählerModal(false);
    }, [dataZähler, selectedRowZähler]);
    

    依赖项数组[dataZ?hler,selectedRowZ?hler]确保每次更改时回调都具有这些依赖项的最新值。如果没有这一点,回调可能会使用dataZ hler和selectedRowZ hler的过时值。