ReactJs - component nesting to display nested data working abruptly - javascript

My data is:
[{
"id": 1,
"label": "List item 1",
"parent_id": 0,
"children": [{
"id": 5,
"label": "List item 1",
"parent_id": 1
},
{
"id": 6,
"label": "List item 1",
"parent_id": 1
},
{
"id": 7,
"label": "List item 1",
"parent_id": 1
},
{
"id": 8,
"label": "List item 1",
"parent_id": 1,
"children": [{
"id": 9,
"label": "List item 1",
"parent_id": 8
},
{
"id": 10,
"label": "List item 1",
"parent_id": 8
}
]
}
]
},
{
"id": 2,
"label": "List item 1",
"parent_id": 0
}
]
My App.js components looks like this:
import React, {useState} from 'react';
import {apiData} from './data/api';
import './App.css';
import {NestedLists} from './components/NestedLists';
const App = () => {
return (
<div className="container">
<h3>Calling a nested component</h3>
<NestedLists filteredData = {data} />
</div>
)
}
export default App;
NestedList component:
import React from 'react'
export const NestedLists = ({filteredData}) => {
return (
<ul>
{filteredData && filteredData.map((m,i) => {
return (
<li key={m.label}>
{m.id}
{m.children && <NestedLists filteredData={m.children} />}
</li>
);
})}
</ul>
)
}
When i check the results there is same nesting printed twice as shown in the below image which should not happen::
Here inside 1: 5,6,7,8 is repeated twice and inside 8 : 9,10 is repeated twice which should not happen. what is missing here?

It is working fine.check where are you making mistake. i have tried this
import React from 'react';
const App = () => {
const data = [{
"id": 1,
"label": "List item 1",
"parent_id": 0,
"children": [{
"id": 5,
"label": "List item 1",
"parent_id": 1
},
{
"id": 6,
"label": "List item 1",
"parent_id": 1
},
{
"id": 7,
"label": "List item 1",
"parent_id": 1
},
{
"id": 8,
"label": "List item 1",
"parent_id": 1,
"children": [{
"id": 9,
"label": "List item 1",
"parent_id": 8
},
{
"id": 10,
"label": "List item 1",
"parent_id": 8
}
]
}
]
},
{
"id": 2,
"label": "List item 1",
"parent_id": 0
}
]
const NestedLists = ({filteredData}) => {
return (
<ul>
{filteredData && filteredData.map((m,i) => {
return (
<li key={m.label}>
{m.id}
{m.children && <NestedLists filteredData={m.children} />}
</li>
);
})}
</ul>
)
}
return (
<div className="container">
<h3>Calling a nested component</h3>
<NestedLists filteredData = {data} />
</div>
)
}
export default App;
I think you are not passing right data here in your App.js file
import {apiData} from './data/api';
<NestedLists filteredData = {data} />
change it to
<NestedLists filteredData = {apiData} />

Related

Rendering more than one RadioButton.Group in react native cannot be treated separately

in my application i have two arrays as follow:
const items =[
{
"id": 1,
"groupId": 1,
"name": "product1",
"price": 10
},
{
"id": 2,
"groupId": 1,
"name": "product2",
"price": 10
},
{
"id": 3,
"groupId": 2,
"name": "product3",
"price": 10
},
{
"id": 4,
"groupId": 2,
"name": "product4",
"price": 10
}
];
const category = [
{
"name": "optional",
"id": 1,
"type": 1
},
{
"name": "required",
"id": 2,
"type": 1
},
]
So, what I am doing I take the category name from array category then after displaying the name of category I am rendering the item that belong to that category from the items array when category.id = items.groupId
I did the following to achieve that:
{items.filter(product => product.groupId === category.id).map(b => {
return (<View >
<RadioButton.Group
key={b.groupId}
value={value}
onValueChange={value => { addToOrder(value) }}
>
<View>
<RadioButton.Item
key={b.id}
mode='android'
label={b.name}
value={b}
/>
<Text>${b.price}</Text>
</View>
</RadioButton.Group>
</View>
)
})
}
So the result was as expected it gave two groups of radio buttons
Results looks like:
[ ] refer to the circle of the button
optional
- porduct1 $10 [ ]
- porduct2 $10 [ ]
required
- porduct3 $10 [ ]
- porduct4 $10 [ ]
However,
the problem I am facing is that when i check on of the item in the first group and then i check another item in the second group the value in first group is unchecked. I spent several hours trying to solve it but I'm unable to make it work.
the library for radioButtons is RadioButton from 'react-native-paper';
I will appreciate any help. thanks

How to test this component with react testing library - just a starter with this new testing library

I have 2 components that i am learning to test with react-testing-library but i am stuck in the middle.
My components is NestedLists.js
import React from 'react'
export const NestedLists = ({filteredData}) => {
return (
<ul>
{filteredData && filteredData.map((m,i) => {
return (
<li key={i}>
{m.id}
{m.children && <NestedLists filteredData={m.children} />}
</li>
);
})}
</ul>
)
}
and filteredData props is an array with the following values ::
export const filteredData = [{
"id": 1,
"label": "List item 1",
"parent_id": 0
},
{
"id": 9,
"label": "List item 9",
"parent_id": 8
},
{
"id": 8,
"label": "List item 8",
"parent_id": 1
},
{
"id": 5,
"label": "List item 5",
"parent_id": 1
},
{
"id": 6,
"label": "List item 6",
"parent_id": 1
},
{
"id": 7,
"label": "List item 7",
"parent_id": 1
},
{
"id": 10,
"label": "List item 10",
"parent_id": 8
},
{
"id": 2,
"label": "List item 2",
"parent_id": 0
}
]
I have done some tests but those are not working. Kindly suggests some of the tests that i can do on the above component with testing library::
My tests that are npt working or failing is below:
NestedLists.test.js
import React from 'react';
import { render, waitForElement } from '#testing-library/react';
import {NestedLists} from '../components/NestedLists';
import {apiData} from '../fixtures'
test('renders NestedLists components', async () => {
const { getByText } = render(<NestedLists filteredData = {apiData} />);
await waitForElement(() => {
expect(getByText(props.filteredData[0].id)).toBeTruthy();
})
});
You probably don't need test for such simple component but if you still want to add test you can do something like below.
You might need to import jest-dom from testing library to use toBeInTheDocument
import React from 'react';
import { render} from '#testing-library/react';
import {NestedLists} from '../components/NestedLists';
import {apiData} from '../fixtures'
test('renders NestedLists components', async () => {
const { findByText} = render(<NestedLists filteredData = {apiData} />);
const firsListItem = await getByText(apiData[0].id);
epxect(firstListItem).toBeInTheDocument()
});
You can test like all elements are rendered. Render the component using render from #testing-library/react by passing proper props and check all elements are present
Here is the codesandbox - https://codesandbox.io/s/sad-merkle-lpnrw
You can see test in test tab
import React from "react";
import { render } from "#testing-library/react";
import { NestedLists } from "./App";
const filteredData = [
{
id: 1,
label: "List item 1",
parent_id: 0
},
{
id: 9,
label: "List item 9",
parent_id: 8
},
{
id: 8,
label: "List item 8",
parent_id: 1
},
{
id: 5,
label: "List item 5",
parent_id: 1
},
{
id: 6,
label: "List item 6",
parent_id: 1
},
{
id: 7,
label: "List item 7",
parent_id: 1
},
{
id: 10,
label: "List item 10",
parent_id: 8
},
{
id: 2,
label: "List item 2",
parent_id: 0
}
];
it("renders search screen without crash", () => {
const { getByText } = render(<NestedLists filteredData={filteredData} />);
expect(getByText("1")).toBeDefined();
expect(getByText("9")).toBeDefined();
expect(getByText("8")).toBeDefined();
expect(getByText("5")).toBeDefined();
expect(getByText("6")).toBeDefined();
expect(getByText("7")).toBeDefined();
expect(getByText("10")).toBeDefined();
expect(getByText("2")).toBeDefined();
});

I dont know how to use map in this situation

I wrote the contents of this JSON to the array "heroes", but I don't know how to get "damage", "basicAttack.category" and "specialAttack.category" for mapping. I tried to solve this problem but unfortunately I don't know how.
{
"id": 20,
"name": "Warrior",
"skills": [
{
"id": 1,
"basicAttack": {
"id": 2,
"name": "Hit1",
"category": "weakAttack"
},
"specialAttack": {
"id": 16,
"name": "Special1",
"category": "spellAttack"
},
"damage": 200
},
{
"id": 2,
"basicAttack": {
"id": 3,
"name": "Hit2",
"category": "rangeAttack"
},
"specialAttack": {
"id": 17,
"name": "Special2",
"category": "fightAttack"
},
"damage": 100
}
]
}
and this is my way of mapping and what data I'm trying to get
const item = this.state.heroes.skills.map(item => {
/*i dont have idea how map
<p>{item.damage}</p>
<p>{item.specialAttack.cathegory} + {item.basicAttack.category}</p>
*/
})
Just map over the heroes.skills and you will find the result how to access the value
var heroes ={
"id": 20,
"name": "Warrior",
"skills": [
{
"id": 1,
"basicAttack": {
"id": 2,
"name": "Hit1",
"category": "weakAttack"
},
"specialAttack": {
"id": 16,
"name": "Special1",
"category": "spellAttack"
},
"damage": 200
},
{
"id": 2,
"basicAttack": {
"id": 3,
"name": "Hit2",
"category": "rangeAttack"
},
"specialAttack": {
"id": 17,
"name": "Special2",
"category": "fightAttack"
},
"damage": 100
}
]
}
heroes.skills.map(hero=>{
console.log("damage...........",hero.damage)
console.log("basicAttack.category.........",hero.basicAttack.category)
console.log("specialAttack.category........",hero.specialAttack.category)
})
if you want to render then you have to return it and then render
const heroesDiv = this.state.heroes.skills.map((hero) => (
<>
<p>{item.damage}</p>
<p>{item.specialAttack.category} + {item.basicAttack.category}</p>
</>
))
this.state.heroes.skills.map((item) => {
return `<div><p>${item.damage}</p><p>${item.specialAttack.category} ${item.basicAttack.category}</p></div>`
})
let data = {
"id": 20,
"name": "Warrior",
"skills": [
{
"id": 1,
"basicAttack": {
"id": 2,
"name": "Hit1",
"category": "weakAttack"
},
"specialAttack": {
"id": 16,
"name": "Special1",
"category": "spellAttack"
},
"damage": 200
},
{
"id": 2,
"basicAttack": {
"id": 3,
"name": "Hit2",
"category": "rangeAttack"
},
"specialAttack": {
"id": 17,
"name": "Special2",
"category": "fightAttack"
},
"damage": 100
}
]
}
this is how you can map
return (
<div>
{data.skills.map(item=>
<div key={item.id}>
<h3>{item.id}: {item.basicAttack.name}-{item.basicAttack.category}, {item.specialAttack.name}-{item.specialAttack.category}</h3>
</div>)}
</div>
)
const item = this.state.heroes.skills.map(item => {
return(
<React.Fragment key={item.id}>
<p>{item.damage}</p>
<p>{item.specialAttack.id}</p>
<p>{item.basicAttack.id}</p>
<p>{item.damage}</p>
<React.Fragment>
)
})
You can use JSON.parse to parse the json data and you can easily map as shown in the working snippet below.
var data='{"id":20,"name":"Warrior","skills":[{"id":1,"basicAttack":{"id":2,"name":"Hit1","category":"weakAttack"},"specialAttack":{"id":16,"name":"Special1","category":"spellAttack"},"damage":200},{"id":2,"basicAttack":{"id":3,"name":"Hit2","category":"rangeAttack"},"specialAttack":{"id":17,"name":"Special2","category":"fightAttack"},"damage":100}]}';
var jsondata=JSON.parse(data);
console.log(jsondata.skills[0].damage);
console.log(jsondata.skills[0].basicAttack['category']);
console.log(jsondata.skills[0].specialAttack['category']);
You can do it as this
const item = this.state.heroes.skills.map(item => {
return (
<React.Fragment>
<p>{item.damage}</p>
<p>{item.specialAttack.category} + {item.basicAttack.category}</p>
</React.Fragment>
);
})
Hope it helps

Parsing categories tree into HTML select tag

I have this categories tree input :
"categories": [
{
"text": "Upstate",
"id": 3,
"category_parent_id": 0,
"children": []
},
{
"text": "North",
"id": 2,
"category_parent_id": 0,
"children": [
{
"text": "Child N 1",
"id": 5,
"category_parent_id": 2,
"children": [
{
"text": "Another Child 1",
"id": 11,
"category_parent_id": 5,
"children": []
},
{
"text": "Another Child 2",
"id": 10,
"category_parent_id": 5,
"children": []
}
]
},
{
"text": "Activity",
"id": 4,
"category_parent_id": 2,
"children": []
}
]
},
{
"text": "Health",
"id": 1,
"category_parent_id": 0,
"children": [
{
"text": "Good Health",
"id": 9,
"category_parent_id": 1,
"children": []
},
{
"text": "Bad Health",
"id": 8,
"category_parent_id": 1,
"children": []
}
]
}
]
So, now I want to populate my select box like this :
Upstate
North
-Child N 1
--Another Child 1
--Another Child 2
-Activity
Health
-Good Health
-Bad Health
So, how can I parse through the input tree and populate the select box with these values? Any algorithm or recursive function approach I can use to achieve this ?
make a recursive function
flatCategories(data: any[], children: any[], index: number) {
data=data||[];
children.forEach(x => {
data.push({ id: x.id, text: '-'.repeat(index) + x.text });
if (x.children && x.children.length)
this.flatCategories(data, x.children, index + 1)
})
return data
}
You can use like
let dataFlat=this.flatCategories([], this.data.categories, 0);
console.log(this.dataflat.map(x => x.text))
If you want to create a recursive component it's easy (but in case of select not work)
#Component({
selector: 'item-line',
template: `
<div *ngFor="let item of children" [style.margin-left]="index+'rem'">
{{item.text}}
<item-line *ngIf="item.children" [children]="item.children" [index]="index+1">
</item-line>
</div>
`,
})
export class HelloComponent {
#Input() children:any[]
#Input() index:number=0;
}
You can see in stackblitz

Make subcategory as array in object of array

I'm working on django rest and angular. This json array is coming from server and contains category and subcategory values.
my code will create category and related subcategory in separate keys of array. But i want keep subcategory as a array in object in same object.
Result should be like this:
[{"title":"title of category","sub":[array of related sub]} , ...]
mycode:
public data = SERVERRESPONE;
public categories = [];
this.data.filter(c => c.parent_id === null).map(c => <{ title: {}; subcategories: {} }>{
title: {"title":c.title},
subcategories: this.data.filter(sc => sc.parent_id === c.cat_id).map(sc => sc.title)
}).forEach(c => {
this.categories.push([c.title, [c.subcategories]]);
});
server response :
[
{
"id": 5,
"cat_id": 0,
"parent_id": null,
"title": "web development"
},
{
"id": 6,
"cat_id": 1,
"parent_id": null,
"title": "android development"
},
{
"id": 7,
"cat_id": null,
"parent_id": 0,
"title": "php"
},
{
"id": 8,
"cat_id": null,
"parent_id": 1,
"title": "java"
}
]
it is nice question but it has very easy solution!
const array = [
{
"id": 5,
"cat_id": 0,
"parent_id": null,
"title": "web development"
},
{
"id": 6,
"cat_id": 1,
"parent_id": null,
"title": "android development"
},
{
"id": 7,
"cat_id": null,
"parent_id": 0,
"title": "php"
},
{
"id": 8,
"cat_id": null,
"parent_id": 1,
"title": "java"
}
]
let result = []
for (let key of array) {
if (key.parent_id === null) {
let new_key = key,
sub = []
for (let iterator of array)
if (iterator.parent_id === key.cat_id)
sub.push(iterator)
new_key.sub = sub
result.push(new_key)
}
}
console.log(result)

Categories