React:从按钮 onclick 传递映射数据以显示在另一个组件上
Posted
技术标签:
【中文标题】React:从按钮 onclick 传递映射数据以显示在另一个组件上【英文标题】:React: passing mapped data from button onclick to display on another component 【发布时间】:2020-12-02 06:34:37 【问题描述】:我在传递从 API 映射的数据时遇到了一些困难,它会根据 API 数组中的事件数量显示一堆事件卡片。
这是我的事件卡组件;
export default function EventCard()
const classes = useStyles();
const [data, setData] = useState([]);
useEffect(() =>
const fetchData = async () =>
const result = await axios("http://localhost:23455/Event");
setData(result.data);
;
fetchData();
, []);
const handleClick = (value) => () =>
console.log(value);
;
return (
<div>
<Row>
" "
data.map((item) => (
<Card
className=classes.root
style= marginRight: "25px", marginBottom: "25px"
>
<CardHeader
avatar=
<Avatar aria-label="recipe" className=classes.avatar>
item.icon
</Avatar>
action=
<IconButton aria-label="settings">
<MoreVertIcon />
</IconButton>
title=item.name
subheader="September 14, 2016"
/>
<CardMedia
className=classes.media
image=LordsLogo
title="Paella dish"
/>
<CardContent>
<Typography variant="body2" color="textSecondary" component="p">
<p key=item.id> item.description</p>
<p key=item.id> item.startDate</p>
<p key=item.id> item.endDate</p>
</Typography>
</CardContent>
<CardActions disableSpacing>
<IconButton aria-label="add to favorites">
<Button variant="outlined">Steward</Button>
</IconButton>
<IconButton aria-label="share">
<Button variant="outlined" onClick=handleClick( item )>
Tickets
</Button>
</IconButton>
</CardActions>
</Card>
))
</Row>
</div>
);
我有一个 onclick 函数,它记录哪些数据被添加到“value”onclick 中,如果我点击一张卡片,控制台会记录该特定卡片的值:
我现在要做的是在另一个名为 ServiceForm 的组件中使用此信息。我希望能够点击按钮,链接到ServiceForm,并使用ServiceForm组件中“Item”中的变量,理想如下图;
<Form.Group as=Col controlId="formGridForeName">
<Form.Label>**item.description**</Form.Label>
<Form.Control
name="firstName"
placeholder="Enter name"
onChange=this.handleChange
/>
</Form.Group>
EventCard 如果功能组件和 ServiceForm 是基于类的组件,我如何将信息从第一个传递到后者?谢谢
编辑:显示组件层次结构
ServiceForm在ServiceApplication中渲染,如图:
import * as React from "react";
import ServiceForm from "../components/ServiceForm";
class ServiceApplication extends React.Component
render()
return (
<React.Fragment>
<h1>Service Application</h1>
<h6>Users can use this page to apply for tickets.</h6>
<ServiceForm />
</React.Fragment>
);
export default ServiceApplication;
EventCard组件在EventsPage中渲染,如下图;
import EventCard from "../components/EventCards/EventCard";
class EventsPage extends React.Component
render()
return (
<React.Fragment>
<h1>Events</h1>
<h6>
Welcome to the Events, here you can apply for seats or tickets at the
events shown below.
</h6>
<Row>
<EventCard />
</Row>
</React.Fragment>
);
export default EventsPage;
这个想法是在点击 EventCard 上的“Tickets”按钮时传递 ID(ID 是从 API 中提取并映射的)。
【问题讨论】:
onClick=handleClick( item ) 改为 onClick=() => handleClick( item ) 请提及层次结构,ServiceForm 是子组件还是父组件? @Roy.B 我改变了它,但是现在点击什么都没有记录 @devd ServiceForm 将是我想的父组件 看起来这两个组件是相互独立的。你必须使用 redux 来实现你的目标。 【参考方案1】:这是一个你想如何在你的应用中真正存储状态的问题。
举一个简单的例子,其中一个组件是另一个组件的子组件。在这种情况下,父母可以在本地存储状态,例如useState() 并将其作为道具传递给孩子。
const SomeParent = () =>
const [isTrue, setIsTrue] = React.useState(true)
return (
<Child isTrue=isTrue />
)
在某些情况下,您可能希望跨多个组件或跨整个应用共享状态。在这种情况下,您有很多不同的选择。
1.将状态提升到您需要的最高点。
将在多个组件之间共享的状态移动到两个组件共享的最高点是最简单的。 例如
Section component <- store state here
Parent one
child one
child two
Parent two
child one
在这里,您可以再次使用 useState() 或将状态存储在 useReducer() 中并向下传递调度。
然后数据流将如下所示:
-
在部分组件中初始化状态,例如const [someState, setSomeState]
将 setSomeState 回调传递给父组件,然后将子组件作为道具传递,例如
在子组件设置状态 onClick 使用回调例如onClick = () => 动作(项目)
将状态值传递给您的第二个父组件,然后依次传递给子组件,例如
您可以访问表单中的状态
这可以是一个对象或多个状态值。随着复杂性的增加,我倾向于使用 useReducer()。
2。在您的应用程序中实现 useContext 或 Redux 以创建可从任何组件访问的应用程序状态(这是一个更大的解释,每个组件都有很多资源......
注意:我不知道你为什么在这里使用两组括号:
const handleClick = (value) => () =>
console.log(value);
;
当您调用 handleClick 时,如果您要传递一个值,则需要将其切换为箭头函数:
onClick=handleClick( item )
...
onClick=() => handleClick(item)
【讨论】:
【参考方案2】:试试这个:
const handleClick = (value) =>
console.log(value);
;
<Button variant="outlined" onClick=() => handleClick( item ) />
【讨论】:
【参考方案3】:基本上,您想通过传递 item prop 从功能组件触发类组件函数,如果它是父子组件,您可以将函数作为 prop 传递,并在需要时从父组件或子组件触发它。
但在你的情况下,两个组件都没有连接,你可以不使用 redux 做的是为 ex 创建一个公共本地存储,我们称之为 localStorage.js :
var PublicData = (function()
var data = null;
var getData = function()
return data;
;
var setData = function(props)
data = props;
;
var clearData = function()
data = null;
;
return
GET: getData,
SET: setData,
CLEAR: clearData
)();
export default PublicData;
然后你导入它并像下面这样使用:
import PublicData from './PublicData.js';
PublicData.SET(apiData); // you set your data once you fetch it
const data = PublicData.GET(); // get data
PublicData.CLEAR(); // clear your data
注意:这不是一个实际的 localStorage,但它的工作原理是一样的,它将帮助您在组件之间共享变量。
【讨论】:
我认为建议你应该编写自己的数据存储函数来完成 useReducer 或 useContext 设计的工作是不好的建议。 我给了两个选项,第一个是将回调函数作为道具发送给子组件并从那里触发它,但是没有任何连接,所以你必须以另一种方式共享它,这将解决他的问题,即使它不是最好的解决方案,而您建议只传递一个不会以任何方式解决问题的简单道具,然后您对我的答案投了反对票,甚至无法区分 localstorage 和 reducer 或 context !即使您认为他们做同样的工作,这又是一个坏建议吗?【参考方案4】: Parent
/ \
Service Event
我从您的问题中猜测,您的应用树的简化看起来像上面。
您遇到的问题如下:
Parent
/ \
Service Event
Data
在自上而下的架构中,服务不知道事件和该分支中的任何数据。
所以你有 2 个选项(可能更多,但这 2 个选项让你走得很远):
-
提升数据
Parent
Data
/ \
Service Event
-
将数据存储在其他地方并提供访问机制
Data
Parent
/ \
Service Event
选项 1 可以通过传递 props(数据)和函数(使用 props 传递)来操作数据(或者,实际上,首先填充它,即获取它)来实现 p>
流程看起来像: 父级是有状态的。父级将数据的当前状态传递给关心它的子级(在您的情况下,它看起来像服务和事件)。 Parent 向 Event 传递一个函数,该函数可以附加到元素的 Event 子树中的单击处理程序。
由于向下传递一个函数,当调用该单击处理程序时,它可以设置父级的状态,正常的自上而下渲染流程将从该状态处理设置更新并将数据更改向下传递,从而它们将(通常)调用重新渲染。
我认为Recoil 使用了这种方法,并且随着我们希望拆分应用程序并避免全局状态管理,它再次变得越来越流行。
选项 2 是 Redux、Cerebral、MobX 等数据管理库的所在地。如果您熟悉发布者/订阅者模式,那么您就会知道它们是如何工作的。
本质上,这些库为您的应用程序保存共享状态,例如您在此处拥有的数据。然后,它们(通常)为您提供一种模式来管理状态更改(通过已发布的事件)并确保订阅该数据的组件接收新状态并进行更新。
这里的“讨论”与选项 1 不同: 点击处理程序发布一个事件,本质上,它要求对数据进行一些操作。操作发生(或不发生),并且订阅者被更新。在您的情况下,单击将告诉集中存储(即任何监听)获取数据,当它完成并且数据更改(从空到填充)时,它允许所有相关元素(或者,很可能,整个应用程序树并利用自上而下的渲染)了解它,他们可以做他们需要做的事情。
您选择哪个选项完全取决于您,它们各有利弊。
祝你好运,想想你的数据,你如何存储它,以及你如何使用它!
【讨论】:
【参考方案5】:很高兴您提出这个问题,因为这是一个已经被多次解决和解决的问题。其他贡献者已经写了很好的综合答案,所以我会保持简短。
1。将共享声明移动到一个共同的祖先。
这个很直截了当,但很快就会变得非常多毛。
2。使用上下文 API
您可以使用 react 的上下文 API 将负责获取数据的代码移动到您的上下文中,并在您的应用中的任何位置使用该数据,而不会带来太多麻烦。这种方法现在很流行。 Take a look at this 和 here's a sweet library to help you get started。
3。使用状态管理库(首选)
您可以选择使用任何您喜欢的状态管理库来解决这个问题。有很多选择,redux
是最受欢迎的选择,这是有充分理由的。它使您能够实现数据和 UI 之间的关注点分离,但有点冗长。
Mobx
也很受欢迎,并且拥有良好的开发者体验,但我个人不喜欢它强制你的代码使用某种风格。
如果您不熟悉响应和状态管理,那么这些库可能真的令人生畏。我建议你从zustand 开始。它超级简单,可以完成工作。 ;)
【讨论】:
【参考方案6】:您可以简单地使用Custom Event
https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent。这不是最佳实践,但它确实有效。在EventCard
中,当用户单击时,您调度一个自定义事件,其有效负载为item
,然后ServiceForm
将侦听该事件并将item
存储为状态。工作演示在这里
https://codesandbox.io/s/distracted-aryabhata-ec6gc
事件卡
const handleClick = () =>
const item =
description: "This is description"
;
const event = new CustomEvent("customEvent",
detail: item
);
window.dispatchEvent(event);
;
服务表格
const [item, setItem] = useState(null);
...
useEffect(() =>
const handler = (e) =>
setItem(e.detail);
;
window.addEventListener("customEvent", handler);
return () => window.removeEventListener("customEvent", handler);
, []);
【讨论】:
以上是关于React:从按钮 onclick 传递映射数据以显示在另一个组件上的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 onClick 方法通过 react-router 传递道具