react js : display items using map method - reactjs

i want to display submodule names also inside div
this is my api data
"predefined": [
{
"id": 2,
"mainModule": "bonding",
"description": "some random description 2",
"sub_module": [
{
"id": 3,
"subModuleName": "activity of bonding",
"completed": false
},
{
"id": 4,
"subModuleName": "self care",
"completed": false
}
]
},
{
"id": 1,
"mainModule": "main module 1",
"description": "some random description",
"sub_module": [
{
"id": 1,
"subModuleName": "sub module 1",
"completed": false
},
{
"id": 2,
"subModuleName": "sub module 2",
"completed": false
}
]
}
],
this is my axios to fetch the data from api and set my state called items
axios
.get(
"url",
config
)
.then((res) => {
this.setState({ items: res.data.predefined });
});
}
this is the jsx i have used to display my api here personData.mainmodule and personData.description works fine since submoule names are inside an arrray not rendering i cannot use it as {personData.sub_module[0].subModuleName}
{this.state.items.map((personData) => {
return (
<>
<div className="activity">
<h3>{personData.mainModule}</h3>
<span>{personData.description}</span>
{this.state.item.map((personData) => {
return (
<>
{personData.sub_module.subModuleName} //error
</>
);
})}
</div>
</>
);
})}

You should use current personData variable to access sub_module properties. And then you use another Array.map to render it.
{
personData.sub_module.map(item => {
return (
<>
{item.subModuleName}
</>
);
})
}

{
personData.sub_module.map(item => {
return (
<>
{item.subModuleName}
</>
);
})
}

You don't need to iterate this.state.items again to get sub_module. You can do like this
{this.state.items.map((personData) => {
return (
<>
<div className="activity">
<h3>{personData.mainModule}</h3>
<span>{personData.description}</span>
{personData.sub_module.map((data) => {
return <div>{data.subModuleName}</div>;
})}
</div>
</>
);
})}
This way you can iterate sub_module properly.

Related

Getting MUI's Autocomplete to correctly display categories and subcategories

I'm trying to essentially achieve the following image which is found here:
In that thread, they talk about the best way to display categories and subcategories and the consensus is an MUI Autocomplete.
I'm not however sure how I would achieve something like that at all and would like some help with how I could achieve it.
What I need is for the user to only be able to select one category, whether it be a "root category" or a sub-category. So in the example above, either the "Boysenberry" or the "Brulee Berry".
I also want to try and have the id of said category so I can apply it on my back end (which I'm sure I can do.
My fetched json structure looks like the below:
[
{
"id": 1,
"name": "Audio Visual Equipment",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 2,
"name": "Projectors",
"stockItems": [],
"childCategories": [
{
"id": 3,
"name": "Lenses",
"stockItems": [],
"childCategories": []
}
]
}
]
},
{
"id": 4,
"name": "Lighting Equipment",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 5,
"name": "Intelligent",
"stockItems": [],
"childCategories": []
},
{
"id": 6,
"name": "Generic",
"stockItems": [],
"childCategories": []
},
{
"id": 7,
"name": "Control",
"stockItems": [],
"childCategories": []
}
]
},
{
"id": 8,
"name": "Sound Equipment",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 9,
"name": "Mixing Desk",
"stockItems": [],
"childCategories": []
}
]
},
{
"id": 10,
"name": "Cables",
"parent": null,
"stockItems": [],
"childCategories": [
{
"id": 11,
"name": "Multicore",
"stockItems": [],
"childCategories": []
},
{
"id": 12,
"name": "Lighting",
"stockItems": [],
"childCategories": []
},
{
"id": 13,
"name": "Audio",
"stockItems": [],
"childCategories": []
},
{
"id": 14,
"name": "Video",
"stockItems": [],
"childCategories": []
},
{
"id": 15,
"name": "Power",
"stockItems": [],
"childCategories": []
}
]
}
]
EDIT:-
I get the following warning when I refresh the page:
MUI: The value provided to Autocomplete is invalid.None of the options match with `-1`.You can use the `isOptionEqualToValue` prop to customize the equality test.
When I then click on the Autocomplete, I get the "root" categories only. When I then click on one, the name is not shown and I get the following error:
MUI: The value provided to Autocomplete is invalid.None of the options match with `1`.You can use the `isOptionEqualToValue` prop to customize the equality test.
1. Flattening the List
My approach is to "flatten" the list of categories into a single array so that MUI can evaluate each sub-category. Each of my flat options has a depth property so that I can display it with the correct level of indentation.
We can use the code from the Checkboxes example and add an indentation with the MUI sx prop:
renderOption={(props, option, { selected }) => (
<li {...props}>
<Checkbox checked={selected} sx={{ ml: 2 * option.depth }} />
{option.name}
</li>
)}
2. Filtering Matches
I'm assuming that we want to display the top-level category above a sub-category which matches on the sub-category term only. Like in your linked "ber" example, if the category was "Fall Gold" and the subcategory was "Fall Gold Berry". This means that we should consider the child terms when deciding if a term is a match.
To achieve this, I am including a matchTerms property on all option objects and using a custom filterOptions function on the Autocomplete which looks at this property. With the createFilterOptions utility, we just need to determine what texts to examine:
filterOptions={(createFilterOptions({
// join with some arbitrary separator to prevent matches across adjacent terms
stringify: (option) => option.matchTerms.join("//")
}))}
3. Highlighting
The last piece of this is the highlighting, which is not included in MUI. The MUI docs recommend the autosuggest-highlight package and include an example of how to use it. We can copy that, changing option.title to option.name.
Complete Code
JavaScript
import {
Autocomplete,
TextField,
Checkbox,
createFilterOptions
} from "#mui/material";
import { data } from "./data";
import parse from "autosuggest-highlight/parse";
import match from "autosuggest-highlight/match";
const toOptions = (category, depth = 0, parentId = null) => {
const { id, name, childCategories = [] } = category;
const children = childCategories.flatMap((child) =>
toOptions(child, depth + 1, id)
);
const option = {
id,
name,
depth,
parentId,
matchTerms: [name].concat(children.map((obj) => obj.name))
};
return [option].concat(children);
};
const optionsList = data.flatMap((category) => toOptions(category));
export default () => {
return (
<Autocomplete
options={optionsList}
getOptionLabel={(option) => option.name}
renderOption={(props, option, { selected, inputValue }) => {
const matches = match(option.name, inputValue);
const parts = parse(option.name, matches);
return (
<li {...props}>
<Checkbox checked={selected} sx={{ ml: 2 * option.depth }} />
<div>
{parts.map((part, index) => (
<span
key={index}
style={{
fontWeight: part.highlight ? 700 : 400
}}
>
{part.text}
</span>
))}
</div>
</li>
);
}}
renderInput={(params) => <TextField {...params} />}
filterOptions={createFilterOptions({
// join with some arbitrary separator to prevent matches across adjacent terms
stringify: (option) => option.matchTerms.join("//")
})}
/>
);
};
TypeScript
import {
Autocomplete,
TextField,
Checkbox,
createFilterOptions
} from "#mui/material";
import { data } from "./data";
import parse from "autosuggest-highlight/parse";
import match from "autosuggest-highlight/match";
// describes the input data
type Category = {
id: number;
name: string;
childCategories?: Category[];
};
// describes the format that we want
interface Option {
id: number;
name: string;
depth: number;
parentId: number | null;
matchTerms: string[];
}
const toOptions = (
category: Category,
depth: number = 0,
parentId: number | null = null
): Option[] => {
const { id, name, childCategories = [] } = category;
const children = childCategories.flatMap((child) =>
toOptions(child, depth + 1, id)
);
const option = {
id,
name,
depth,
parentId,
matchTerms: [name].concat(children.map((obj) => obj.name))
};
return [option].concat(children);
};
const optionsList: Option[] = data.flatMap((category) => toOptions(category));
export default () => {
return (
<Autocomplete
options={optionsList}
getOptionLabel={(option) => option.name}
renderOption={(props, option, { selected, inputValue }) => {
const matches = match(option.name, inputValue);
const parts = parse(option.name, matches);
return (
<li {...props}>
<Checkbox checked={selected} sx={{ ml: 2 * option.depth }} />
<div>
{parts.map((part, index) => (
<span
key={index}
style={{
fontWeight: part.highlight ? 700 : 400
}}
>
{part.text}
</span>
))}
</div>
</li>
);
}}
renderInput={(params) => <TextField {...params} />}
filterOptions={createFilterOptions({
// join with some arbitrary separator to prevent matches across adjacent terms
stringify: (option) => option.matchTerms.join("//")
})}
/>
);
};
CodeSandbox Link

Having problems parsing json complex data in react js. Map error

App.js
I am facing this issue file error - Uncaught TypeError: items.data.map is not a function. I have tried some other options but did not work. I cant seem to find what I am doing wrong.
.then((res) => res.json())
.then((json) => {
this.setState({
items: json,
DataisLoaded: true
});
})
}
render() {
const { DataisLoaded, items } = this.state;
if (!DataisLoaded) return <div>
<h1> Loading data ... </h1> </div> ;
return (
<div className = "App">
<h1> Fetch data from an api in react </h1> {
items.data.map((item) => (
<ol key = { item.data} >
Continents: {item.data[0]}
</ol>
))
}
</div>
);
}
}
export default App;
JSON Data
Nested API data from json data type.
{
"data": {
"data": [
{
"project_id": "xxxx",
"title": "xxx34",
"description": "xxx23",
"expense": 1699126,
"budget": 6418516,
"country": "xxx",
"sector": [
{
"name": "Accelerate structural transformations",
"code": "18"
}
],
"sdg": [
{
"name": "Peace, justice, and strong institutions",
"id": "16"
}
],
"signature_solution": [
{
"name": "Strengthen effective, inclusive and accountable governance",
"id": "2"
}
],
"donor": [
"Australian DFAT",
"GLOBAL FUND TO FIGHT AIDS, TUBERCULOSIS",
"UNITED NATIONS DEVELOPMENT PRO"
],
"marker": [
"Hows",
"Joint Programme",
"Partner",
"Whos",
"COVID-19 Response"
]
},
{
],
"links": {
"next": null,
"previous": null
},
"count": 44
},
"status": 200,
"success": true
}
I tried data.data.map but still facing the same error. What am I doing wrong here?
Firstly, the TypeError you got is syntax error. The implementation of an arrow function must follow by curly brackets
items.data.map((item) => (
<ol key = { item.data} >
Continents: {item.data[0]}
</ol>
))
to
items.data.map((item) => {
<ol key = { item.data} >
Continents: {item.data[0]}
</ol>
})
Secondly, items you are mapping is a nest of JSON object - key: value pair. It's not suitable for mapping.
The mapping iterator or iterating an array is perfect when used to retrieve
data from a sequence item have the same or similar structure.
E.g:
const arr = [{"id": "1", "name": "a"}, {"id": "2", "name": "b"}, {"id": "3", "name": "c"}];
arr.map((item) => {
console.log(item.id);
console.log(item.name);
})
You should pretreat your data first.

.map is not a function with MaterialUI Tab component

So I'm trying to populate a in-memory MirageJS' database with a post request of the data in useEffect, making a get request after. Then I save the result to columnsNames state and use it to map this array and read the information as a label of Tab MaterialUI component. However, I have some questions:
When I put a console.log after useEffect or inside get request, both response.data and columnsNames state have 2 identical arrays. Why 2 and not 1?
At runtime the console prints
Uncaught TypeError: columnsNames.map is not a function
The above error occurred in the <ScrollableTabsButtonAuto> component
and nothing is showed in the screen. Why?
Anyone that could help me, thanks in advance!
Data.ts
export const data = [
{
"id": 1,
"title": "Test 1",
"steps": [
"Read a starting book",
"Keep calm and drink coffee",
"Don't Panic so much",
]
},
{
"id": 2,
"title": "Test 2",
"steps": [
"Get some helpful help",
"Don't loose yourself",
]
},
{
"id": 3,
"title": "Test 3",
"steps": [
]
},
{
"id": 4,
"title": "Test 4",
"steps": [
]
},
{
"id": 5,
"title": "Test 5",
"steps": [
]
},
{
"id": 6,
"title": "Test 6",
"steps": [
]
}
]
App.tsx
import { createServer, Model } from 'miragejs';
createServer({
models: {
columns: Model,
},
routes() {
this.namespace = 'api'
this.get('columns', () => {
return this.schema.all('columns')
})
this.post('columns', (schema, request) => {
const data = JSON.parse(request.requestBody)
return schema.create('columns', data)
})
}
})
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Tab.tsx
interface ColumnTypes {
id: number,
title: string,
steps: string[]
}
export default function ScrollableTabsButtonAuto() {
const [value, setValue] = React.useState(0);
const [columnsNames, setColumnsNames] = React.useState<ColumnTypes[]>([])
const handleChange = (event: React.SyntheticEvent, newValue: number) => {
setValue(newValue);
};
React.useEffect(() => {
api.post('columns', data)
.then( () => {
api.get<ColumnTypes[]>('columns')
.then(response => setColumnsNames(response.data))
})
}, [])
return (
<Box sx={{ maxWidth: { xs: 320, sm: 1280 }, bgcolor: 'background.paper' }}>
<Tabs
value={value}
onChange={handleChange}
variant="scrollable"
scrollButtons="auto"
aria-label="scrollable auto tabs example"
>
{columnsNames.map(({ id, title }) => (
<Tab
label={title}
key={id}
/>)
)}
</Tabs>
</Box>
);
}

How to map a nested JSON response in React

I have to map the nested JSON result in React. The below one is the response I got as response from backend API.
{
"id": 2,
"name": "sanjna",
"email": "vv#gmail.com",
"address": "iiiii, hhh",
"gender": "1",
"tagline": "Friendly to all",
"description": "4 years experience in programming",
"languages": "English ",
"key_skills": [
{
"id": 1,
"name": "RUBY ON RAILS, HTML, BOOTSTRAP, JQUERY, REACT",
"relevant_experience": "4"
},
{
"id": 2,
"name": "ruby",
"relevant_experience": "2"
}
],
"certifications": [
{
"id": 1,
"name": "MCA",
"institution_name": "vvv unversity",
"certification_date": "20-12-2020",
"image": null
},
{
"id": 2,
"name": "HTML training",
"institution_name": "nnn unversity",
"certification_date": "20-12-2022",
"image": null
}
],
"educations": [
{
"id": 1,
"qualification": "MCA",
"course": "Masters Degree PG",
"institute": "kkk",
"ins_location": "jjjj",
"passing_year": "2015"
}
]
}
This is my React code to get this response
const [singleUserDetail, setsingleUserDetail] = React.useState('');
let logged_user_id = job_seeker.actable_id;
const getsingleUserDetails = (logged_user_id) => {
axios
.get(`http://localhost:3001/users/${logged_user_id}`, { withCredentials: true })
.then((response) => {
const singleUserDetail = response.data;
setsingleUserDetail(response.data)
console.log(response.data); //prints the above JSON results in console
})
.catch((error) => {
console.log(" error", error);
});
};
React.useEffect(() => {
getsingleUserDetails(logged_user_id);
}, [logged_user_id]);
When I prints {singleUserDetail.name} it gives me result sanjna
{singleUserDetail.email} it gives me result vvv#gmail.com
{singleUserDetail. address} it gives me result iiiii, hhh. from my above JSON result
But how to print keyskills, certifications and educations here with mapping. I'm a beginner in React.
Youu can do something like this, my example is very simple,but you can complicate this as much as you need, with conditions or loops in loops.
<div>
{singleUserDetail.name},{singleUserDetail.email},{singleUserDetail.address},{singleUserDetail.gender},{singleUserDetail.tagline},{singleUserDetail.description}, {singleUserDetail.languages}
</div>
{singleUserDetail.key_skills.map((skill)=>{
return(
<div key={skill.id}>
{skill.name}:{skill.relevant_experience}
</div>
);
})}
{singleUserDetail.certifications.map((certification)=>{
return(
<div key={certification.id}>
{certification.name},{certification.institution_name},{certification.certification_date}
</div>
);
})}
{singleUserDetail.educations.map((education)=>{
return(
<div key={education.id}>
{education.qualification},{education.course},{education.institute}
</div>
);
})}
For more information, React documentation

Map through Array object stored in state

Im trying to access data from an API and render the results. Im storing each object in a separate state and im trying to map through each state, however this throws the following Error.
My code is:
state = {
events: [],
items: [],
isLoading: true
}
// fetchData = () => {
componentDidMount() {
const url = 'http://www.mocky.io/v2/5c8673f0340000981789c0da'
axios.get(url).then(response => {
this.setState({ items: response.data, isLoading: false, events: response.data[0] })
console.log(this.state.events)
})
}
render() {
return(
<div>
<div>
<h1>26/2</h1>
<ul>
{ this.state.events.map(event => (<li>{event.activity}</li>)) }
</ul>
</div>
<div>
<h1>27/2</h1>
<ul>
</ul>
</div>
</div>
)
}
}
API structure:
[
{
"id": 1,
"activity": "lunch",
"startDate": "2019-02-26 12:00",
"endDate": "2019-02-26 13:00",
"location": "Lagerhuset"
},
{
"id": 2,
"activity": "meeting",
"startDate": "2019-02-26 22:00",
"endDate": "2019-02-27 07:00",
"location": "online"
},
{
"id": 3,
"activity": "meeting",
"startDate": "2019-02-26 10:00",
"endDate": "2019-02-26 12:00",
"location": "Lagerhuset"
}
]
Now im wondering if what I'm trying to achieve even is possible? And if so, what am I doing wrong? Thanks.
items contain the array of items. events just contain the first item which is an object. Hence the map fails.
Either use :
{this.state.items.map(event => (
<li>{event.activity}</li>
))}
or this :
<li>{this.state.events.activity}</li>

Resources