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