繁体   English   中英

使用 React、Typescript 和 useContext 添加一个元素

[英]Add an element with React, Typescript and useContext

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

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

据我了解,我需要:

  • 创建类似 AppContext.tsx 的东西
  • 使用 createContext() 创建上下文 // 不确定 arguments 我必须用 Typescript 把它放在这里
  • 创建提供者? // 也不确定
  • 用上下文提供者包装我的两个组件

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

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中,您只需要该列表,因此您可以使用setItemList从上下文中仅导入useContext

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>
  );
}

但我建议您将您的提供商放置在您的订阅者组件最近的地方。
例如,您可以创建一个将在浏览器路由器中使用的页面组件并将提供程序放入其中,如下所示:
项目列表页面.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 中的上下文提供程序,它应该如下所示:
应用程序.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;

编辑饥饿的napier-t4rcq

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM