上下文挂钩不能存储值

context hook cant store value

下面的代码工作正常,除非调用 Add 函数将新数字添加到数组, 数组仅包含最后添加的项目。

不明白为什么它不存储 ID

App.ts

import MyComponent from "./main";
import { FavoriteContext, usePostsContextValue } from "./store";

function App() {
  const postsContextValue = usePostsContextValue();
  return (
    <FavoriteContext.Provider value={postsContextValue}>
        <MyComponent />
      </Container>
    </FavoriteContext.Provider>
  );
}

export default App;

组件:

import { FavoriteContext } from "../store";
...

function MyComponent() {
  const [data, setData] = useState<certificates[]>([]);
  const [loading, setLoading] = useState<boolean>(false);
  const [pageNumber, setPageNumber] = useState<number>(1);
  const { IDs, Add, Remove } = useContext(FavoriteContext);



  useEffect(() => {
    console.log("IDs", IDs); //logs only 1 item, last selected Id
  }, [IDs]);

  const handleAddClick= async (id: number) => {
    Add(id);
  };


  const columns: TableColumn<certificates>[] = useMemo(() => {
    return [
      {
        name: "STATUS",
        cell: (row) => <strong>{status(row.status)}</strong>,
      },
      {
        cell: (row) => {
          return (
            <IconButton onClick={() => handleAddClick(row.id)}>
              <BookmarkBorderIcon />
            </IconButton>
          );
        },
        button: true,
      },
    ];
  }, []);

  return (
    <DataTable
      columns={columns}
      data={data}
      pagination
      paginationServer
      paginationTotalRows={totalRows}
      onChangeRowsPerPage={handlePerRowsChange}
      onChangePage={handlePageChange}
    />
  );
}

export default MyComponent;

Context.ts:

import React, { useCallback, useMemo, useState } from "react";
import { favouriteType } from "../models";

export const defaultValue: favouriteType = {
  IDs: [],
  Add: (id: number) => {},
  Remove: (id: number) => {},
};
export const FavoriteContext = React.createContext<favouriteType>(defaultValue);

function usePostsContextValue(): favouriteType {
  const [IDs, setIDs] = useState<number[]>([]);

  const Add = (id: number) => {
    setIDs([...IDs, id]);
  };

  const Remove = (id: number) => {
    const newPosts = [...IDs];
    const removedPostIndex = newPosts.findIndex((f) => f === id);
    if (removedPostIndex > -1) {
      newPosts.splice(removedPostIndex, 1);
    }
    setIDs(newPosts);
  };

  return {
    IDs,
    Add,
    Remove,
  };
}

export { usePostsContextValue };

每次添加新 ID 时,都会创建一个新的 Add 函数来捕获新 ID 的值。

  const Add = (id: number) => {
    setIDs([...IDs, id]); // <-- this capture the ID of the outer scope
  };

这按预期工作,但是查看下面的组件:

  const columns: TableColumn<certificates>[] = useMemo(() => {
    return [
      {
        name: "STATUS",
        cell: (row) => <strong>{status(row.status)}</strong>,
      },
      {
        cell: (row) => {
          return (
            <IconButton onClick={() => handleAddClick(row.id)}>
              <BookmarkBorderIcon />
            </IconButton>
          );
        },
        button: true,
      },
    ];
  }, []);

由于此组件永远不会重新呈现,因此 Add 函数不会更新,因此它始终是捕获的 IDs 为空 [] 的第一个版本。因此添加一个 ID 总是:

const Add = (id: number) => {
    setIDs([...[], id]); // the first vesion of Add captured IDs = [];
  };

并且始终只有最后一个 ID。

解决方案是包含 handleAddClick(它捕获 useMemo dep 数组中的新 Add 函数:

  const columns: TableColumn<certificates>[] = useMemo(() => {
    return [
      {
        name: "STATUS",
        cell: (row) => <strong>{status(row.status)}</strong>,
      },
      {
        cell: (row) => {
          return (
            <IconButton onClick={() => handleAddClick(row.id)}>
              <BookmarkBorderIcon />
            </IconButton>
          );
        },
        button: true,
      },
    ];
  }, [handleAddClick]);

然而,这破坏了 useMemo 的目的,因为 Add/handleAddClick 总是在每次渲染时重新创建,所以解决方案是将它们都包装在 useCallback 中.

  const Add = useCallback((id: number) => {
    setIDs([...IDs, id]);
  }, [IDs]); // notice I have to include IDs here so that the function can be recreated with the new IDs.

  const handleAddClick= useCallback(async (id: number) => {
    Add(id);
  }, []);

更好的是,您可以使用 setState 的函数语法,这样就无需在 dep 数组中包含 IDs,从而提高性能。

  const Add = useCallback((id: number) => {
    setIDs((prevIDs) => [...prevIDs, id]); // the function form will receive the previous state as the argument.
  }, []);