I am learning React and this is my first message to Stack Overflow. Could you help me with the following problem which I have been trying to solve for days and through many tutorials?
I am doing a React project where App is the parent component and in a Bootstrap layout are its children Material-UI nested Sidebar and Container. So I should be able to raise onClick events from the Sidebar menu (return product_id of a clicked product ListItem) to the App component to update/ change product data in the Container.
May be because of my App.js is not class but function App() my code does not accept this and props. So when I have been following many tutorials I often get an error: this (or props) in not defined. In addition to that in the Material UI sidebar code the onClick of ListItem is already in use of its onClick function: <ListItem onClick={onClick} button dense>
. So when I should pass a reference onSelection={handleSelection}
from the parent I can't replace {onClick} with {onSelection} in the child. I tried also reference to both onClick function and onSelection in the Sidebar component: <ListItem onClick={onClick; onSelection} button dense>
but it is not working. Right now in the Sidebar there is an attempt to react to both events:
function combo(e) {
onClick(e);
onSelection(e);
}
triggered by <ListItem onClick={combo} ...>
which does not work (because onSelection is not a function).
How should I solve this? Here are my pieces of code:
App.js (Parent)
import "./App.css";
import { Container } from "./components/Container";
import Sidebar from "./components/Sidebar";
function App(props) {
const [files, setFiles] = useState([]);
const [items, setItems] = useState([]);
let product_id = 13; /* Hard coded test value */
/* Get sidebar */
useEffect(() => {
fetch("/sidebar")
.then((res) => res.json())
.then((data) => {
console.log(data);
setItems(data);
});
}, []);
/* Get product data */
useEffect(() => {
fetch(`/data/${product_id}`)
.then((res) => res.json())
.then((data) => {
setFiles(data);
});
}, []);
function handleSelection() {
console.log("Hello");
}
return (
<React.Fragment>
<header className="header">
<h5>Header</h5>
</header>
<div className="container-fluid">
<div className="row">
<div className="col-3">
<Sidebar
className="sidebar"
items={items}
product_id={product_id}
onSelection={handleSelection}
/>
</div>
<div className="col-6">
<Container files={files} />
</div>
</div>
</div>
</React.Fragment>
);
}
export default App;
Sidebar.js (Child)
import { Collapse, List, ListItem } from "@material-ui/core";
import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ExpandLessIcon from "@material-ui/icons/ExpandLess";
function SidebarItem({ item, product_id }) {
const [collapsed, setCollapsed] = useState(true);
const { title, items, id } = item;
function toggleCollapse() {
setCollapsed((prevValue) => !prevValue);
}
function onClick() {
if (Array.isArray(items)) {
toggleCollapse();
}
product_id = id;
console.log(product_id); /* This is working well */
}
let expandIcon;
if (Array.isArray(items) && items.length) {
expandIcon = !collapsed ? <ExpandLessIcon /> : <ExpandMoreIcon />;
}
function combo(e) {
onClick(e);
onSelection(e);
}
return (
<>
<ListItem onClick={combo} button dense>
<div>{title}</div>
{expandIcon}
</ListItem>
<Collapse
className="sidebar-subitem-text"
in={!collapsed}
timeout="auto"
unmountOnExit
>
{Array.isArray(items) ? (
<List disablePadding dense>
{items.map((subItem, index) => (
<SidebarItem key={`${subItem.id}${index}`} item={subItem} />
))}
</List>
) : null}
</Collapse>
</>
);
}
function Sidebar({ items }) {
return (
<>
<List disablePadding dense>
{items.map((sidebarItem, index) => (
<SidebarItem
key={`${sidebarItem.title}${index}`}
item={sidebarItem}
/>
))}
</List>
</>
);
}
export default Sidebar;