I coded a table of content using nested components. Each component is a list of headers.
I want to style each component with an indentation effect (margin-left: "20px") to differentiate each level of nesting.
Example:
<Parent>
-->indent <Child/>
-->indent <Child2/>
-->indent (etc.)
</Parent>
Any idea of how to do it dynamically?
Here's my code:
import React from "react";
const TocContent = ({ props }) => {
return (
<div className="TOC">
{props.TOC.map((header) => (
<HeaderList key={header.objectId} header={header} props={props} />
))}
</div>
);
};
const HeaderList = ({ header, props }) => {
return (
<div>
<li
onMouseDown={(e) => e.stopPropagation()}
className="listing"
style={{}}
onClick={(e) =>
props.handleHeaderClick(
header.level,
header.treepath,
header.containsLaw,
header.sections,
header.secNum,
header.objectId,
header.id,
e.stopPropagation(),
)
}
>
{header._id}
</li>
{/* // if savedIndex === CurrentParent Index */}
{props.headerIndex === header.objectId &&
props.headers2.map((node2) => (
<HeaderList key={node2.objectId} header={node2} props={props} />
))}
{props.headerIndex2 === header.objectId &&
props.headers3.map((node3) => (
<HeaderList key={node3.objectId} header={node3} props={props} />
))}
{props.headerIndex3 === header.objectId &&
props.headers4.map((node4) => (
<HeaderList header={node4} key={node4.objectId} props={props} />
))}
</div>
);
};
export default TocContent;
Put the margin (or padding) on the element that contains both the HeaderList's main content and the sub-HeaderList components (instead of just the main content as you have now). Specifically this would be the div that wraps all other returned content in the HeaderList component. The margins will stack up and each nested header list will be more indented than the parent.
For example (just HTML & CSS):
.header-list {
margin-left: 20px;
}
<div class="header-list">
First Element
<div class="header-list">
Second Element
<div class="header-list">
Third Element
</div>
</div>
</div>
Related
I need to dynamically change the class to "active". I do this by checking indexes. The problem is that the active class changes IMMEDIATELY in all displayed li, and not in one particular one. This only happens when the logic is in the parent component, if you move the useState and onTypeActive to the child component everything works fine.
Parent component
const [activeSyze, setActiveSyze] = useState(0);
const onSyzeActive = (index) => {
setActiveSyze(index);
};
return (
<>
{pizzaJson.map((item, index) => (
<PizzaBlock
key={item.id}
onSyzeActive={(index) => onSyzeActive(index)}
activeSyze={activeSyze}
{...item}
item={item}
indexPizza={index}
/>
))}
</>.
Child comp
return (
<ul>
{sizes.map((size, index) => (
<li
key={index}
onClick={() => onSyzeActive(index)}
className={activeSyze === index ? "active" : ""}
>
{size} см.
</li>
))}
</ul>
)
I have an onClick in map function, if I click onClick it will change state of all map item not that item which I clicked. I am using useState hook.
const [open, setOpen] = useState(true);
{
FilterAvailable.map((item, id) => {
return (
<li className="filterItem">
<div
className="filterBtn flex align-center justify-between"
onClick={() => setOpen(!open)}
>
<h2>{item.catogery}</h2>
<div>
{open ? (
<IoIosArrowDown className="downArrow" />
) : (
<IoIosArrowUp className="downArrow" />
)}
</div>
</div>
{item.options.map(option => {
return (
<>
{open && (
<ul className="filterOption">
<li className="filterOptionitem">
<button>{option}</button>
</li>
</ul>
)}
{/* <div className="hrLine"></div> */}
</>
);
})}
</li>
);
});
}
I want to open only those item option which click.
Your state is in the wrong place, you need to break the component and have a open state per filterableItem
const Filters = () => {
return FilterAvailable.map((item, id) => {
return <FilterItem item={item} />;
});
};
const FilterItem = ({ item }) => {
const [open, setOpen] = useState(true);
return (
<li className="filterItem">
<div
className="filterBtn flex align-center justify-between"
onClick={() => setOpen(!open)}
>
<h2>{item.catogery}</h2>
<div>
{open ? (
<IoIosArrowDown className="downArrow" />
) : (
<IoIosArrowUp className="downArrow" />
)}
</div>
</div>
{item.options.map(option => {
return (
<>
{open && (
<ul className="filterOption">
<li className="filterOptionitem">
<button>{option}</button>
</li>
</ul>
)}
</>
);
})}
</li>
);
};
Put the index inside state on onclick , inside map check if state index equals to map index and you are done
Consider moving the mapped elements to their own component with props - then you create and set your open and closed state there.
I am trying to map a return statement with multiple child components and getting ESLint error
console.error
Warning: Each child in a list should have a unique "key" prop.
This is my React Code:
const EmployeeDetails = ({ employeeid }) => {
return (
<div>
<strong>Employee Overview Details</strong>
</div>
<div className="flex-container" key="test">
{employeeOverviewDetails.map((detail, index) => {
return (
<>
{detail.heading === 'Employee ID' && (
<div
className="indiviual-flex"
key={index}
>
<small key={detail.heading}>
{detail.heading}
</small>
<br />
<strong key={detail.value}>
{employeeid}
</strong>
</div>
)}
{detail.heading !== 'Employee ID' && (
<div
className="indiviual-flex"
key={detail.heading}
>
<small>{detail.heading}</small>
<br />
<strong>{detail.value}</strong>
</div>
)}
</>
)
})}
</div>
)
}
I am passing key for every element and yet ESLint keeps warning about unique "key" prop
You are missing a fragment:
const EmployeeDetails = ({ employeeid }) => {
return (
<>
<div>
<strong>Employee Overview Details</strong>
</div>
<div className="flex-container" key="test">
{employeeOverviewDetails.map((detail, index) => {
return (
<>
{detail.heading === "Employee ID" && (
<div className="indiviual-flex" key={index}>
<small key={detail.heading}>{detail.heading}</small>
<br />
<strong key={detail.value}>{employeeid}</strong>
</div>
)}
{detail.heading !== "Employee ID" && (
<div className="indiviual-flex" key={detail.heading}>
<small>{detail.heading}</small>
<br />
<strong>{detail.value}</strong>
</div>
)}
</>
);
})}
</div>
</>
);
};
You cannot have 2 elements side by side in React, either use this syntax
<> </>
to wrap around the elements which are side by side or use you can import Fragment from React like this:
import { Fragment } from "react";
And then wrap the elements which are side by side with Fragment like this:
<Fragment></Fragment>
or if u don't want to import Fragment you can also use React.Fragment. but you need to import react for this.
I am trying to build a reusable accordion, i was able to create an accordion with one level, but here i am stuck to have the nested accordion.
What i have tried so far
App.js
import "./styles.css";
import Accordion from "./Accordion";
import LIST from './Constants';
const listMaker = (item) => {
let faqItem;
if (item.children.length === 0) {
faqItem = (
<>
<Accordion title={item.name}></Accordion> <hr />
</>
);
} else {
let faqItemChildren = item.children.map((item) => {
let faqItem = listMaker(item);
return (
<>
${faqItem}
<hr />
</>
);
});
faqItem = <Accordion title={item.name}>{faqItemChildren}</Accordion>;
}
return faqItem;
};
let listItems = LIST.map((item) => {
let menuItem = listMaker(item);
return menuItem;
});
export default function App() {
return listItems;
}
have added codesandbox
I am new tor react, Any help is appreciated
Instead of using dangerouslySetInnerHTML you can use the children, as you need is a spread of React.ReactChildren. That would be just calling the {children} from props instead of the dangerouslySetInnerHTML
<div className="accordion__section">
<button className={`accordion ${setActive}`} onClick={toggleAccordion}>
<p className="accordion__title">{title}</p>
<Chevron className={`${setRotate}`} width={10} fill={"#777"} />
</button>
<div
ref={content}
style={{ maxHeight: `${setHeight}` }}
className="accordion__content"
>
{children}
</div>
</div>
Here is a forked solution of your codesandbox.
Also, Instead of setting the DOM to a variable, as its a conditional scenario, you can use the ternary operator, which helps in better readability.
const listMaker = (item) => {
return (
<>
{item.children.length === 0 ? (
<Accordion title={item.name} />
) : (
<Accordion title={item.name}>
{item.children.map((childItem) => {
return listMaker(childItem);
})}
</Accordion>
)}
</>
);
};
dangerouslySetInnerHTML is to use with strings. You shouldn't give an array of components to it. Yet you don't send any prop called content anyway. I think you meant children prop there. Just render children instead of using dangerouslySetInnerHTML
In your Accordion component replace this:
<div
className="accordion__text"
dangerouslySetInnerHTML={{ __html: props.content }}
/>
With this:
<div className="accordion__text">
{ props.children }
</div>
There is a component responsible for rendering items in the e-commerce. I want to have a single component, which will render different content depending on the category clicked by user.
My solution looks something like this:
function ProductsPage({collections}) {
return (
<Route path="/products/type_one" render={() =>
<div>
<h1>Type One</h1>
<div className="products">
{
collections.category.typeOne.items.map((item) => {
return (
<ProductCard key={item.id} item={item} />
)
})
}
</div>
</div>
}/>
<Route path="/products/type_two" render={() =>
<div>
<h1>Type Two</h1>
<div className="products">
{
collections.category.typeTwo.items.map((item) => {
return (
<ProductCard key={item.id} item={item} />
)
})
}
</div>
</div>
}/>
)
}
So far I have only four subcategories, that`s why it looks pretty harmless. But if there are dozens of them, then the component code will be clogged with an endless copy-paste. Is there a more elegant method for solving the problem?
You can use route props to dynamically get the category parameter from the route URL and display the items using so. Your code might look similar to this:
<Route path="/products/:category" component={Category} />
function Category(props) {
const category = collections.category[props.match.params.category]
if (category) {
return (
<div>
<h1>category.name</h1>
<div className="products">
{
category.items.map((item) => {
return (
<ProductCard key={item.id} item={item} />
)
})
}
</div>
</div>
)
} else {
// category does not exist
}
}