React recursion - can't get correct value - javascript

I'm having Array of Objects named items. Each Object looks like this
interface IItem {
id: string;
title: string;
items: IItem[] | null
}
So I'm doing recursion, as in
parent component:
{items?.map((folder, index) => (
<Item
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
console.log(
event.target.innerHTML,
folder.id,
folder.title
);
}}
key={folder.id}
data={folder}
/>
))}
WhileItem component looks like this:
const nestedItems =
data.items
? data.items.map((item, index) => (
<Item
onClick={onClick}
key={item.id}
data={item}
/>
))
: null;
return (
<div>
<button onClick={onClick}>
<Typography>
{data.title} {data.id}
</Typography>
</button>
<div>{nestedItems}</div>
</div>
Html output and whats rendered in browser looks correct,
but when I try to get {id}, or {title} from item in parent component
the output of any click which has parent doesn't log it's own id, but it's parent id.
The strange thing is that console.log from above on any "not first level items" looks like this:
event.target.innerHTML - correct value
folder.id, folder.title - has parent ? parent id value : it's own id value
So I'm not really sure what's happening and where is the catch :/
Thanks in advance!

You only define onClick for each of the root-level Items and then within the Item component you just pass the onClick prop to the child. Thus every child has the same onClick function as its parent.
To fix this you can change onClick to be more generic by accepting data (the id and/or other information) rather than having data curried into it. This way the onClick function passed to the Item works for any item, but the onClick function passed to the button within each Item is unique to that item.
For example:
Parent component:
{items?.map((folder, index) => (
<Item
onClick={(event, clickedFolder) => {
event.stopPropagation();
event.preventDefault();
console.log(
event.target.innerHTML,
clickedFolder.id,
clickedFolder.title
);
}}
key={folder.id}
data={folder}
/>
))}
Item component:
const nestedItems =
data.items
? data.items.map((item, index) => (
<Item
onClick={onClick}
key={item.id}
data={item}
/>
))
: null;
return (
<div>
<button onClick={event => onClick(event, data)}>
<Typography>
{data.title} {data.id}
</Typography>
</button>
<div>{nestedItems}</div>
</div>
)

Related

Passing child state to parent in React //

I'm looking to pass my state back up from a child component to parent. And yes I know there is similar online! however this is specifically with the formkit component, and I cannot resolve it.
I have a parent component, which contains a FrameworkList, this is an iterable dropdown which creates an array.
Please see parent:
<MainContainerWrapper>
<AccordionContainer>
<FrameworkList ref={parent}/>
</AccordionContainer>
</MainContainerWrapper>
And with this, I have a child component. This component has an array called items. When items is changed the state is updated and the array is modified. My goal is to have this array based to parent component so I can complete some Redux dispatch events on an onSubmit. However, if there's a better way to do this in the child component, let me know.
Child:
export default forwardRef(function list(props, ref) {
THIS IS THE STATE, items, TO BE PASSED UP.
const [items, item, addItem, input, sortUp, sortDown, sortList, remove] =
UseListFunctions([
{ id: 0, name: 'Transparency' },
{ id: 1, name: 'Collaboration' },
{ id: 2, name: 'Flexible working arrangements' },
]);
// console.log(items)
return (
<StageComponent data-has-animation={ref ? true : null}>
<div className="logo">
{(!ref && (
<img
src="https://cdn.formk.it/web-assets/logo-auto-animate.svg"
width="300"
height="37"
/>
)) ||
''}
</div>
<ULComponent ref={ref}>
{items.map((item) => (
<ListComponent key={item.id}>
{/* text is here */}
{/* <span>{item.name}</span> */}
<Typography variant="subtitle1" color="black">
{item.name}
</Typography>
<div className="action-icons">
<button onClick={() => sortUp(item)}>
<Arrow direction="up" />
</button>
<button onClick={() => sortDown(item)}>
<Arrow direction="down" />
</button>
<button className="remove" onClick={() => remove(item)}>
<Close />
</button>
</div>
</ListComponent>
))}
<ListComponent>
I hope this is relatively well explained. If anyone knows how to pass this array back up, it would be a life saver.
Alternatively, if someone knows how to utilise redux directly in this child component, that would also help. I don't believe I can use the redux dispatch in this component directly.
I encourage you to lift the child's state to the parent component and pass that state down to the child component(s).

Getting the element that was clicked in functional component in React

I'm new to React and can't get the clicked element.
"this" in functional component doesn't work
function Test(data) {
function getElement() {
}
return (
<div>
{data.map((option: any, index: any) => (
<Text
key={index}
onClick={() => getElement()}
>
{option}
</Text>
))}
</div>
)
}
there are several elements in the data that I want to switch one by one, changing the class 'active', but it is not possible to get the element that was clicked
Be sure to pass the event to your click handler:
function Test(data) {
const handleClick = e => {
const el = e.target
console.log(el)
}
return (
<div>
{data.map((option: any, index: any) => (
<Text
key={index}
onClick={handleClick}
>
{option}
</Text>
))}
</div>
)
}

Highlight One Item in a List at any given time (Index 0 by default)

Sorry for the horrible title, I couldn't for the life of me figure out how to word this problem. By default I want the first item in my list to be highlighted and others to be highlighted when the mouse is over them. This works fine and I was curious how I am able to remove the highlight of that initial item when another is highlighted, I know I need to handle the state within my parent component (List) but I cannot figure it out.
This is my list item that contains some basic state management to show whether the given item is highlighted or not.
export default function Item ({ title, onClick, selected, i }) {
const [highlight, setHighlight] = useState(false)
return (
<ListItem
i={i}
key={title}
selected={selected}
onClick={onClick}
onMouseEnter={() => setHighlight(true)}
onMouseLeave={() => setHighlight(false)}
// i === 0 && no other item is highlighted (need to get this state from the list component)
highlight={highlight || i === 0}
>
{title}
</ListItem>
)
}
Parent component that takes in a 'list' and maps each item in that list to the above component (Item):
export default function List ({ list, onClick, selected, render }) {s
return (
<div>
{render ? (
<ListContainer>
{list.map((item, i) => (
<Item
i={i}
key={item.title}
title={item.title}
onClick={() => onClick(item.title)}
selected={selected(item.title)}
/>
))}
</ListContainer>
) : null}
</div>
)
}
Here is a Gyazo link that shows the current implementation, what I want to achieve is for that initial item to no longer be highlighted when the mouse has entered another item where the index does not equal 0.
You need to lift your highlight state to parent List component
function List ({ list, onClick, selected, render }) {
const [highlightIndex, setHighlightIndex] = setState(0);
return (
<div>
{render ? (
<ListContainer>
{list.map((item, i) => (
<Item
i={i}
key={item.title}
title={item.title}
onClick={() => onClick(item.title)}
selected={selected(item.title)}
highlightIndex={highlightIndex}
setHighlightIndex={setHighlightIndex}
/>
))}
</ListContainer>
) : null}
</div>
)
}
function Item ({ title, onClick, selected, i, highlightIndex, setHighlightIndex }) {
return (
<ListItem
i={i}
key={title}
selected={selected}
onClick={onClick}
onMouseEnter={() => setHighlightIndex(i)}
highlight={i === highlightIndex}
>
{title}
</ListItem>
)
}
you can do it with css
ul:not(:hover) li:first-of-type,
ul li:hover {
background-color: red;
}
the first one will be highlighted while not hovered, and the other will be highlighted when hovered and "cancel" the first one.
Presuming the selected one will go to the top of the list.

Nested loop to return simple ListItem in Reactjs - -Object.values and Object.keys

Really stumped. I am trying to create a ListItem for every key of every value in an array of objects. When I log item, it returns the key I'm looking for as a string. Great! However, the list items never render on the page.
return (
<div>
<List className="list">
{Object.values(props.sectionInfo).forEach(section => {
Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'> //doesn't render, but also doesn't throw errors
<ListItemText primary={item} />
</ListItem>
)
});
})}
</List>
</div>
);
console.log(item) //returns "red", "blue"
The below renders the list perfectly, however the list items are the indexes (0, 1)
return (
<div>
<List className="list">
{Object.keys(props.sectionInfo).map((section, index) => {
return (
<ListItem button className='list-item'>
<ListItemText primary={section} />
</ListItem>
)
})}
</List>
</div>
);
Any insight would be helpful.
This is because you are using the forEach in the outer loop and it doesn't return anything actually, so the children prop of the List is undefined. Try this:
return (
<div>
<List className="list">
{Object.values(props.sectionInfo).map(section => {
return Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'>
<ListItemText primary={item} />
</ListItem>
)
});
})}
</List>
</div>
);
Please try to build list of virtual doms as following:
let items = []
Object.values(props.sectionInfo).forEach(section => {
let subItems = Object.keys(section).map((item, index) => {
return (
<ListItem button className='list-item'> //doesn't render, but also doesn't throw errors
<ListItemText primary={item} />
</ListItem>
)
});
items = items.concat(subItems);
})
return (
<div>
<List className="list">
{items}
</List>
</div>
);
Have you tried going through Object.values(section) in the second loop?
Because from your second statement it seems like the contents are indexed as an Array. Maybe you can give more information about the data structure to help you further.

React - Each child in array .. unique "key" prop warning

I appear to have a decent understanding of this principal, which allows me to get by, until now. I am applying a key prop to all children of all iterators, and yet I'm still getting this warning.
A FacilitiesContainer is rendering a FacilitiesComponent, which in turn renders a list of Facilities, which renders a list of Courses. A Course does not use an iterator. However, the FacilitiesContainer is passing the FacilitiesComponent through a HOC, which is returning the final component. There's nothing in the HOC that modifies the passed components, so I'm not sure if this is a problem.
// The render method of FacilitiesContainer
render = () => {
let FacilitiesWithSearch = SearchHOC(
BasicSearch,
FacilitiesComponent,
{data: this.state.facilities }
);
return <FacilitiesWithSearch />;
}
class FacilitiesComponent extends Component {
renderFacilities = () => (
this.props.data.map((facilityData, index) =>
<Facility key={index} data={facilityData} />
)
)
render = () => (
<Grid>
<Row>
<Col xs={12} sm={8} smOffset={2} md={8} mdOffset={1}>
{
this.props.data.length > 0
? this.renderFacilities()
: <div>No results</div>
}
</Col>
</Row>
</Grid>
)
}
const Facility = ({ data }) => (
<Panel>
<Panel.Heading>
<Panel.Title>{data.Name}</Panel.Title>
</Panel.Heading>
<Panel.Body>
<Grid>
<Row>
<p><b>Address:</b><br />
{data.Street}<br />
{data.City}, {data.State} {data.Zip}
</p>
<p><b>Phone:</b> {data.Phone}</p>
{
data.Courses.map((courseData, index) =>
<p><Course key={index} data={courseData} /></p>)
}
</Row>
</Grid>
</Panel.Body>
</Panel>
);
You indeed didn't provide keys to p elements here:
{
data.Courses.map((courseData, index) =>
<p><Course key={index} data={courseData} /></p>)
}
Should be
{
data.Courses.map((courseData, index) =>
<p key={index}><Course data={courseData} /></p>)
}
Try to append a string to the index before assigning it to the key. That's because you are only using index (0,1,2...) both for your list of facilities and list of courses, so there will be duplicated indexes in the final rendered component. If you do as below you ensure that each index is unique:
<Facility key={`facility_${index}`} data={facilityData} />
and
<Course key={`course_${index}`} data={courseData} />

Categories