【问题标题】:Add an element with React, Typescript and useContext使用 React、Typescript 和 useContext 添加一个元素
【发布时间】:2021-07-04 10:29:09
【问题描述】:

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

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

据我了解,我需要:

  • 创建类似 AppContext.tsx 的内容
  • 用 createContext() 创建一个上下文 // 不确定我必须用 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>
  );
}

【问题讨论】:

    标签: reactjs typescript react-hooks react-typescript use-context


    【解决方案1】:

    你可以这样做:
    首先创建一个名为 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;
    
    

    【讨论】:

    • 感谢您的回复!我仍在尝试了解这一切是如何工作的,因此我这次尝试使用 React Router 以更复杂的方式实现它,以应对挑战。因此,我会将您的答案标记为解决方案。再次感谢。顺便说一句,我也是法国人:)
    • 不客气!如果您熟悉 angular 有点像服务,它是在组件之间“共享”道具的解决方案。但是我在某处读到,如果您使用 Suspense 但找不到来源,您可能会遇到上下文问题。抱歉。
    • 我也读过很多关于它的东西。我现在又被路由器和 useContext 卡住了。我用你给我的列表和容器创建了一个 Main.tsx,在 App.tsx 中设置路由,在 Main.tsx 中添加了上下文提供程序,但是当我添加上下文提供程序时,整个 DOM 消失了,没有任何加载。源中也没有任何内容。我也想在另一个页面上显示容器中的内容。奇怪的问题。我的路由器单独工作。您的 useContext 代码单独工作。结合起来,一切都会破裂。
    • 好的,我会更新答案中的沙箱并告诉你什么时候完成
    • 太棒了,谢谢!我所做的是将一个按钮添加到列表容器中,当您单击该按钮时,您会到达一个仅包含已添加项目列表的页面。我编写的代码本身就可以工作(当然没有添加的项目),当我尝试使用 useContext 时,它就会中断。迫不及待地想看看你的实现。由于某种原因,我还没有在网上看到过类似的东西。
    猜你喜欢
    • 2021-07-05
    • 2020-09-29
    • 1970-01-01
    • 2020-07-02
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 2019-05-05
    • 1970-01-01
    相关资源
    最近更新 更多