javascript - 两个功能性 React JS 组件之间的双向通信
问题描述
我在 React 中构建了两个功能组件,一个是Item组件——它包含一些关于东西的数据,带有可选的图形、一些文本数据和价格信息。底部有一个按钮,可让您选择此特定项目。它还在其道具中保存有关当前所选项目ID 的信息——这就是我计划解决此问题的方式。
我的第二个组件是一个ItemList - 它基本上包含一个前面提到的项目列表 - 加上它对所有项目进行排序,并且必须保留有关当前选择哪个组件的信息 - 所选组件基本上看起来不同 - 一些东西,比如边框框和按钮的颜色通过 CSS 切换。
我的实现逻辑是这样的——当用户点击特定项目的“选择”按钮时,项目应该改变它的外观(除非它已经被选中,然后什么都不做),然后以某种方式将信息传播到ItemList上,所以它可以“禁用”先前选择的组件。只能选择一个Item,一旦用户决定选择另一个 Item ,先前选择的应更改其状态并返回未选择的标准图形样式。
我遇到了一个在ItemList组件中具有状态的解决方案,并通过 props 将函数传递给Item,但这并不能解决第二部分 - ItemList需要获取有关更改的信息,因此它可以根据重新渲染所有组件实际状态。我应该深入研究 React API 的哪一部分来解决这个问题?
这是我的组件的代码:
物品
interface Props {
receivedObject: itemToDisplay;
selectedItemId: string;
onClick?: () => void;
}
export default function Item(props: Props) {
const {name, description, price} = props.receivedObject;
const imageUrl = props.receivedObject?.media?.mainImage?.small?.url;
const priceComponent = <Price price={price}/>;
const [isItemSelected, setSelection] = useState(props.selectedItemId == props.receivedObject.id);
const onClick = props.onClick || (() => {
setSelection(!isItemSelected)
});
return (
<>
<div className="theDataHolderContainer">
// displayed stuff goes here
<div className="pickButtonContainer">
// that's the button which should somehow send info "upwards" about the new selected item
<Button outline={isItemSelected} color="danger" onClick={onClick}>{isItemSelected ? "SELECTED" : "SELECT"}</Button>
</div>
</div>
</>)
};
物品清单
interface Props {
packageItems: Array<itemToDisplay>
}
export default function ItemList(props: Props) {
const itemsToDisplay = props.packageItems;
itemsToDisplay.sort((a, b) =>
a.price.finalPrice - b.price.finalPrice
);
let selectedItemId = itemsToDisplay[0].id;
const [currentlySelectedItem, changeCurrentlySelectedItem] = useState(selectedItemId);
const setSelectedItemFunc = () => {
/* this function should be passed down as a prop, however it can only
* have one `this` reference, meaning that `this` will refer to singular `Item`
* how do I make it change state in the `ItemList` component?
*/
console.log('function defined in list');
};
return(
<div className="packageNameList">
<Item
key={itemsToDisplay[0].id}
receivedObject={itemsToDisplay[0]}
onClick={setSelectedItemFunc}
/>
{itemsToDisplay.slice(1).map((item) => (
<Item
key={item.id}
receivedObject={item}
onClick={setSelectedItemFunc}
/>
))}
</div>
);
}
解决方案
在 React 中,数据是向下流动的,因此您最好将状态数据保存在渲染表示组件的有状态组件中。
function ListItem({ description, price, selected, select }) {
return (
<li className={"ListItem" + (selected ? " selected" : "")}>
<span>{description}</span>
<span>${price}</span>
<button onClick={select}>{selected ? "Selected" : "Select"}</button>
</li>
);
}
function List({ children }) {
return <ul className="List">{children}</ul>;
}
function Content({ items }) {
const [selectedId, setSelectedId] = React.useState("");
const createClickHandler = React.useCallback(
id => () => setSelectedId(id),
[]
);
return (
<List>
{items
.sort(({ price: a }, { price: b }) => a - b)
.map(item => (
<ListItem
key={item.id}
{...item}
selected={item.id === selectedId}
select={createClickHandler(item.id)}
/>
))}
</List>
);
}
function App() {
const items = [
{ id: 1, description: "#1 Description", price: 17 },
{ id: 2, description: "#2 Description", price: 13 },
{ id: 3, description: "#3 Description", price: 19 }
];
return (
<div className="App">
<Content items={items} />
</div>
);
}
ReactDOM.render(
<App />,
document.getElementById("root")
);
.App {
font-family: sans-serif;
}
.List > .ListItem {
margin: 5px;
}
.ListItem {
padding: 10px;
}
.ListItem > * {
margin: 0 5px;
}
.ListItem:hover {
background-color: lightgray;
}
.ListItem.selected {
background-color: darkgray;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
推荐阅读
- ab-initio - 有没有办法在 Abinitio 的特定列中处理逗号分隔的数据
- reactjs - 是否可以将 rel=alternate 和 hreflang 与 React.js 一起使用?
- wordpress - 如何更改登录组件,以便它使用注册表单中的新字段而不是用户名或电子邮件(即帐号)?
- html - 之间的空间
- 和子弹
- github - 错误:gpg 未能签署数据致命:未能写入提交对象
- python - 构建 CNN 模型但遭受“张量类型的变量初始化器必须包装在 init_scope 或可调用”中
- javascript - 当我提交某个输入时,我希望我的 html 页面重定向到另一个 html 页面我该怎么做
- reactjs - 如何在构建期间为 mapbox-gl-csp-worker 配置不同的路径
- javascript - 为什么Sonarqube声称如果进行了这些检查(JS),变量可以为空或未定义?
- css-grid - Tailwindcss 流体网格