使用 React、Typescript 和 useContext 添加元素

Add an element with React, Typescript and useContext

我是 Typescript 的新手,对 useContext 挂钩不是很熟悉。基本上,我有两个简单的组件。当我单击它们下方的按钮时,我想将左侧组件中的项目添加到右侧的列表中。我的物品有名称和描述 属性。我只是看着在右边的div旁边显示item.name。

我想尝试使用 useContext 来完成它,但即使在阅读了文档和大量示例之后我也不确定从哪里开始。对于我的小例子来说,它们似乎都太复杂了。

据我了解,我需要:

因此,我们将不胜感激有关该程序的任何提示。谢谢!

function App() {
  return (
    <div className="App">
      <ItemList />
      <ItemContainer />
    </div>
  );
}

我的项目列表组件:

function ItemList() {
  return (
    <div className="itemlist">
      {items.map((item, index) => (
        <div key={index}>
          <div>{item.name}</div>
          <div>{item.description}</div>
          <button>Add to sidebar</button>
        </div>
      ))}
    </div>
  );
}

最后,右边是我的容器:

function ItemContainer() {
  return (
    <div>
      <h1>List of items</h1>
      
      <p>Number of items: {}</p>
      
    </div>
  );
}

你可以这样做:
首先创建一个名为 ItemList.context.tsx 的上下文文件:

import React, {
  createContext,
  Dispatch,
  FunctionComponent,
  useState
} from "react";
import { Item } from "./data";
type ItemListContextType = {
  itemList: Item[]; // type of your items that I declare in data.ts
  setItemList: Dispatch<React.SetStateAction<Item[]>>; //React setState type
};
export const ItemListContext = createContext<ItemListContextType>(
  {} as ItemListContextType
);

export const ItemListContextProvider: FunctionComponent = ({ children }) => {
  const [itemList, setItemList] = useState<Item[]>([]);

  return (
    <ItemListContext.Provider
      value={{ itemList: itemList, setItemList: setItemList }}
    >
      {children}
    </ItemListContext.Provider>
  );
};


然后您可以在父组件中添加上下文提供程序(在您的示例中为 App.tsx):

import "./styles.css";
import ItemList from "./ItemList";
import ItemContainer from "./ItemContainer";
import { ItemListContextProvider } from "./ItemList.context";
export default function App() {
  return (
    <div className="App">
      <ItemListContextProvider>
        <ItemList />
        <ItemContainer />
      </ItemListContextProvider>
    </div>
  );
}

你终于可以通过在你的两个组件中使用挂钩 useContext 来访问你的项目列表:
对于 ItemList.tsx,您需要在其中设置列表(并可选择获取列表以避免将一个项目放置两次):

import { useContext } from "react";
import { data, Item } from "./data";
import { ItemListContext } from "./ItemList.context";

const items: Item[] = data;
export default function ItemList() {
  const { itemList, setItemList } = useContext(ItemListContext); // here you get your list and the method to set the list

  const addItemToItemList = (item: Item) => {
    //you are using the itemList to see if item is already in the itemList
    if (!itemList.includes(item)) setItemList((prev) => [...prev, item]);
  };
  return (
    <div className="itemlist">
      {items.map((item, index) => (
        <div style={{ marginBottom: 15 }} key={index}>
          <div style={{ fontWeight: 800 }}>{item.name}</div>
          <div>{item.description}</div>
          <button onClick={() => addItemToItemList(item)}>
            Add to sidebar
          </button>
        </div>
      ))}
    </div>
  );
}

而在您的 ItemContainer.tsx 中,您只需要列表,这样您就可以仅从 useContext 的上下文中导入 setItemList

import { useContext } from "react";
import { ItemListContext } from "./ItemList.context";

export default function ItemContainer() {
  const { itemList } = useContext(ItemListContext);
  return (
    <div style={{ flexGrow: 4 }}>
      <h1 style={{ textAlign: "center" }}>List of items</h1>

      <p>Number of items: {itemList.length}</p>
      {itemList.length > 0 && (
        <ul>
          {itemList.map((item, i) => (
            <li key={i}>{item.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

更新路由器
如果您希望浏览器路由器位于应用程序的最高位置(例如,对于它们提供程序或暗模式提供程序),您只需要将浏览器路由器包装在上下文提供程序中是完全相同的事情:

export default function App() {
  return (
    <div className="App">
    <ItemListContextProvider> 
        <BrowserRouter>
          <Switch>
            <Route exact path="/" component={HomePage} />
            <Route exact path="/itemList" component={ItemListPage} />
          </Switch>
        </BrowserRouter>
       </ItemListContextProvider> 
    </div>
  );
}

但我建议您将您的提供商放在离订阅者组件最近的地方。
例如,您可以创建一个将在浏览器路由器中使用的页面组件,并将提供者放入其中,如下所示:
ItemListPage.tsx

import React, { FunctionComponent } from "react";
import ItemList from "./ItemList";
import ItemContainer from "./ItemContainer";
import { Link } from "react-router-dom";
import { ItemListContextProvider } from "./ItemList.context";
const ItemListPage: FunctionComponent = () => {
  return (
    <>
     <ItemListContextProvider>
      <h1 style={{ alignSelf: "flex-start" }}>ITEM LIST</h1>
      <Link to="/">homePage</Link>
      <div className="itemListPage">
        <ItemList />
        <ItemContainer />
      </div>
      </ItemListContextProvider>

    </>
  );
};
export default ItemListPage;

当然,您在 App.tsx 中删除了上下文提供程序,它应该看起来像:
App.tsx

import React, { FunctionComponent } from "react";
import ItemList from "./ItemList";
import ItemContainer from "./ItemContainer";
import { Link } from "react-router-dom";
import { ItemListContextProvider } from "./ItemList.context";
const ItemListPage: FunctionComponent = () => {
  return (
    <>
     <ItemListContextProvider>
      <h1 style={{ alignSelf: "flex-start" }}>ITEM LIST</h1>
      <Link to="/">homePage</Link>
      <div className="itemListPage">
        <ItemList />
        <ItemContainer />
      </div>
      </ItemListContextProvider>

    </>
  );
};
export default ItemListPage;