I am desperately trying to generate multiple select inputs for a given JSON from an Backend but I cant make it work. The JSON response I am getting looks smth like this:
{
"selectData": [
{
"id": "ats_2323680",
"label": "Follow up",
"value": "option_id_1"
},
{
"id": "ats_2323701",
"label": "1st Interview, Client",
"value": "option_id_1"
},...
],
"optionData": {
"texts": [
"Sourced",
"On hold",
...
],
"values": [
"option_id_1",
"option_id_2",
]
}
}
Ive already tried several ways and my last attempt looks like this:
Template:
<div v-for="select in selectData" :key="select.id">
<p>{{ select.label }}</p>
<v-select
:items="optionData.texts"
:value="getOptionById(select.value)"
#input="(id) => updateSelect(select, id)"
></v-select>
</div>
Script:
<script>
export default {
data() {
return {
selectData: [],
optionData: {
values: [],
texts: [],
},
};
},
methods: {
fetchData() {
const headers = this.authorizationHeader;
axios
.get(url,
{
headers,
}
)
.then((response) => {
let data = response.data;
this.selectData = data.selectData;
this.optionData = data.optionData;
})
.catch((error) => console.log(error));
},
updateSelect(select, id) {
select.value = id;
},
getOptionById(id) {
let i = this.optionData.values.findIndex((x) => x === id);
return this.optionData.texts[i];
},
},
mounted() {
this.fetchData();
},
};
</script>
I am also not super happy with the JSON struct I am getting. The reason that the optionTextId is also send is, that the optionTexts will be in different languages.
I am really happy with any advise.
I think I solved it. I think it was a classy case of overthinking.
First I changed the JSON structure in the backend like:
{
"selectData": [
{
"id": "ats_2323680",
"label": "Follow up",
"text": "Sourced",
},
{
"id": "ats_2323701",
"label": "1st Interview, Client",
"text": "Kandidaten nachgefasst",
},
...
],
"optionData": {
"texts": [
"Sourced",
"Kandidaten kontaktiert",
...
],
"values": [
"option_id_1",
"option_id_2",
...
]
}
}
Then I changed the Vue code to:
Template:
<div v-for="select in selectData" :key="select.id">
<label for="location">{{ select.label }}</label>
<select id="location" name="location" v-model="select.text">
<option
v-for="option in optionData.texts"
:key="option"
:value="option"
>
{{ option }}
</option>
</select>
</div>
Script:
<script>
export default {
data() {
return {
selectData: [],
optionData: {
values: [],
texts: [],
},
};
},
methods: {
fetchData() {
const headers = this.authorizationHeader;
axios
.get(
url,
{
headers,
}
)
.then((response) => {
let data = response.data;
this.selectData = data.selectData;
this.optionData = data.optionData;
})
.catch((error) => console.log(error));
},
},
mounted() {
this.fetchData();
},
};
</script>
Apparently changing the JSON struc and using v-model did the magic. Might be obvious. Hope this helps a lost soul like me at some point :)
Related
I am creating a product web-app by using vue-2.6.11, axios-0.21.1, vuetify-2.4.3
I am fetching categories from local array then I am passing fetchUrl as Props it into Row component by using v-for . Then in Row component i am fetching the fetchUrl by using axios after getting API response I'm simply mounting it. It working fine but the problem is categories object means Row component loads in random order cause the Row component mounted as it got axios response from API.
So I want Next row await till upper fully-mounted or any thing else to make it orderly loaded.
My Components :
Home.vue -
<template>
<div>
<div v-for="(categories,index) in categories" :key="`${index}`">
<ItemsCarousel
:title="categories.title"
:fetch-url="categories.fetchUrl"
/>
</div>
</div>
</template>
<script>
import categoriesList from '#/local-database/Categories.json';
import ItemsCarousel from '#/components/carousel/ItemsCarousel';
export default {
name: 'Home',
components: {
ItemsCarousel
},
data: () => ({
categories: categoriesList.filter( categories => (catalogue.for==true || categories.for=="home"))
})
}
</script>
ItemsCarousel.vue -
<template>
<div class="items-carousel">
<v-lazy v-model="isActive" :options="{threshold: 0.5}">
<h1>{{title}}</h1>
<div class="items-carousel" v-for="product in products" :key="product.id">
<Card v-bind="{...product}">/>
</div>
</v-lazy>
</div>
</template>
<script>
import ProductManger from '#/mixins/ProductManger';
import Card from '#/components/Card';
export default {
name: 'ItemsCarousel',
mixins: [ProductManger], // Axios Setup
components: {
Card
},
props: ['title','params'],
data: () => ({
isActive: false,
cards: []
}),
methods: {
async loadCard() {
this.contentMangerCore(this.params) // Function code inside mixins
.then(res => {
this.cards = res.data;
})
}
},
mounted() {
this.loadCard();
}
};
</script>
DataSample :-
categoriesList.json-
[{
"id": 1,
"name": "Adventure",
"params": {
"categories": "Adventure",
"sort": "ASC"
}
}, {
"id": 2,
"name": "Art",
"params": {
"categories": "Art",
"sort": "DESC"
}
}, {
"id": 3,
"name": "Beauty",
"params": {
"categories": "Art",
"sort": "DESC"
}
}, {
"id": 4,
"name": "Business",
"params": {
"categories": "Art",
"sort": "DESC"
}
}, {
"id": 5,
"name": "Craft",
"params": {
"categories": "Art",
"sort": "DESC"
}
},...]
products.json-
[{
"name": "AdventureIRC",
"img": "..."
},
{
"name": "Adventie",
"img": "..."
},...]
I Hope you guys will help me to resolve this...
Thank You :smile:
You could make a computed method that determines how many categories to actually display at any given time, incremented by successful axios requests.
get categoriesForDisplay() {
return this.categories.slice(0, this.successfulCarouselFetches + 1)
}
Then define successfulCarouselFetches :
data() {
return {
//
successfulCarouselFetches : 0
}
}
listen for successful axios requests in your Item-Carousel component:
<ItemsCarousel
:title="categories.title"
:fetch-url="categories.fetchUrl"
#success="successfulCarouselFetches = successfulCarouselFetches + 1"
/>
Now broadcast the success whenever your xhr is done working:
methods: {
async loadCard() {
this.contentMangerCore(this.params) // Function code inside mixins
.then(res => {
this.cards = res.data;
this.$emit('success');
})
}
},
When the page loads you'll have a single Item-Carousel component on the page which will perform the first XHR request. When the component $emit's the event, the parent component containing the v-for will increment the successfulCarouselFetches which will allow the getter categoriesForDisplay to add another Item-Carousel within the v-for.
This essentially performs what you're asking for, I believe.
I have JSON data hundreds of entries like this:
{
"product":"Protec",
"type":"Central Opening",
"attribute":"Triple Lock",
"height":"2100",
"width":"1600",
"price":"3000"
},
{
"product":"Protec",
"type":"Sliding Door",
"attribute":"Single Lock",
"height":"2100",
"width":"1600",
"price":"3000"
},
{
"product":"ForceField",
"type":"Hinge Door",
"attribute":"Triple Lock",
"height":"2300",
"width":"1200",
"price":"100"
},
my vue component
var distinct_product = new Vue({
el: '#distinct',
data:{
distinct_product: [],
all_products: []
},
I fetch it and store it in my vue component and store it in a second data so when I render it to the ui the user only sees distinct elements.
mounted: async function(){
fetch("/Data/products.json")
.then(res => res.json())
.then(res => {
this.all_products = res
this.distinct_product = res
var disProduct = [...new Set(this.distinct_product.map(x => x.product))]
var disType = [...new Set(this.distinct_product.map(x => x.type))]
var disAttribute = [...new Set(this.distinct_product.map(x => x.attribute))]
this.distinct_product.productArray = disProduct;
this.distinct_product.typeArray = disType;
this.distinct_product.attributeArray = disAttribute;
My problem is, it also renders elements that aren't available to certain products.
for example a product : 'Window' can't have the attribute : 'triple locks'
I was wondering if I could filter/map the all_products array as the user selects a product.
I looked into computed properties mainly but I'm not sure of a good way to do it. this is my first attempt at a web app and I'm fairly new to JS too.
I aimed to iterate through the array pushing only objects containing the product selected in the UI
atm this is what I've attempted with no luck:
this.distinct_product.product which is bound to the UI
for (var i = 0; i < this.all_products.length; i++){
if (this.all_products[i] === this.distinct_product.product){
this.product.push(i);
return this.product;
}
}
so it would iterate over all_products looking for objects containing this.distinct_product.product which would contain 'Protec' or another product
Am I going at this the wrong way? should I step back in general and try and work with that data a different way?
Sorry if the question is structured poorly it's a skill I'm trying to work on, criticism is welcomed.
You are on the right track. I'll share a simple example so you can understand and make changes to your code accordingly.
var productdata = [
{
"product": "Protec",
"type": "Central Opening",
"attribute": "Triple Lock",
"height": "2100",
"width": "1600",
"price": "3000"
},
{
"product": "Protec",
"type": "Sliding Door",
"attribute": "Single Lock",
"height": "2100",
"width": "1600",
"price": "3000"
},
{
"product": "ForceField",
"type": "Hinge Door",
"attribute": "Triple Lock",
"height": "2300",
"width": "1200",
"price": "100"
},
];
//setTimeout(function () {
distinct_productVue = new Vue({
el: '#distinct',
data: {
//selected: {},
distinct_products: [],
all_products: productdata.map(function (x, index) {
return { text: x.product, value: index + 1 };
}),
selected: '0'
},
computed: {
},
mounted: function () {
this.all_products.unshift({ text: 'Please select a product', value: 0 });
},
methods: {
getDistinctProduct: function () {
var self = this;
self.distinct_products = productdata.filter(function (x, index) {
if (x.product === self.all_products[self.selected].text) {
return { text: x.product, value: index };
}
else { return false; }
});
}
}
});
<html>
<head>
<script src='https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.0/vue.min.js'></script>
</head>
<body>
<div id="distinct">
<select v-model="selected" v-on:change="getDistinctProduct">
<option v-for="option in all_products" v-bind:value="option.value">
{{ option.text }}
</option>
</select>
<!--<span>Selected: {{ selected }}</span>-->
<div v-show="selected != 0" style="margin-top:15px;">
<b>Available products</b>
<div v-for="pro in distinct_products" style="margin-top:15px;">
<div>product: {{pro.product}}</div>
<div>type: {{pro.type}}</div>
<div>attribute: {{pro.attribute}}</div>
<div>height: {{pro.height}}</div>
<div>width: {{pro.width}}</div>
<div>price: {{pro.price}}</div>
</div>
</div>
</div>
</body>
</html>
I'm a newbie in Vue-js and really need your help:
In my Django project I have 2 models: Patient and MedCard of this patient. They are connected with a Foreign Key. I want to implement such functionality: on page "Patients" I have list of patients, then when I push on someone's name I want to see his/her MedCard.
This is my code, but when I push on name I get all records for all patients from MedCard model:
Patients.vue:
<div v-for="patient in patients">
<h3 #click="openMedCard(patient.id)">{{patient.surname}} {{patient.name}}</h3>
<p>{{patient.birth_date}}</p>
</div>
<div
<MedCard v-if="med_record.show" :id="med_record.id"></MedCard>
</div>
export default {
name: 'Patient',
components: {
MedCard,
},
data() {
return {
patients: '',
med_record: {
patient: '',
show: false,
}
}
}
and methods from Patient.vue:
methods: {
openMedCard(id) {
this.med_record.patient = id
this.med_record.show = true
}
MedCard.vue:
<template>
<mu-row v-for="med_record in med_records">
<h3>Doc – {{med_record.doc.surname}}{{med_record.doc.name}}</h3>
<p>{{med_record.patient.surname}}</p>
<p>{{med_record.record}}</p>
<small>{{med_record.date}}</small>
</mu-row>
</template>
export default {
name: 'MedCard',
props: {
id: '',
},
data() {
return {
med_records: '',
}
},
methods: {
loadMedCard() {
$.ajax({
url: "http://127.0.0.1:8000/api/v1/hospital/med_card/",
type: "GET",
data: {
id: this.id,
patient: this.patient
},
success: (response) => {
this.med_records = response.data.data
}
})
}
}
}
loadMedCard() gives me info from all MedCards in JSON like this:
{
"data": {
"data": [
{
"id": 1,
"patient": {
"id": 1,
"surname": "KKK",
"name": "KKK",
"patronymic": "LLL",
"birth_date": "1999-07-07",
"sex": "F",
"phone": "no_phone",
"email": "no_email"
},
"doc": {
"id": 3,
"surname": "DDD",
"name": "DDD",
"patronymic": "DDD",
"education": "d",
"category": "2",
"sex": "m",
"phone": "no_phone",
"email": "no_email"
},
"record": "test text",
"date": "2020-06-09"
}...]
I'll be grateful for any help!
So the API returns you multiple patients's data while you're asking it for just one exact patient. There must be something wrong with the API with the filtering in first place. So you can filter your data on the client side, in your MedCard.vue component. First this component have to show data for one patient only, so the v-for="med_record in med_records" is not needed. Your med_records property can become just an object not an array:
data() {
return {
med_record: {},
}
}
And in the success resolve method of your API call you can filter only the data you need and store it in med_record
success: (response) => {
this.med_records = response.data.data.find((patient)=> { return patient.id === this.id})
}
If you want to store all the data in the med_records, then you can create computed property and apply the same filtering there.
I hope this helps.
I have an object like this and I need to filter the rules within each group item, however I need to also return the group name next to the filtered rule
{
"id": "rulesCompany",
"group": [
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [
{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
},
]
}
For example, I need to return the group "Cadastral/Receita Federal" if I search for "Rece" in search field, but I don't know how to filter data inside data.
What I've done so far:
Module.vue
<template>
<div>
<slide-out :visible.sync="isVisible" :title="text.header">
<div slot="header">
<div>
<button class="neo-bt-icon--big slideout__close--button" #click="isVisible=false">
<svg><use xlink:href="/red/neo-assets/images/simple-line-icons.svg#arrow-2-left"></use></svg>
</button>
<h1 class="slideout__header--text">
{{ text.header }}
</h1>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input type="text" class="neo-form-field" placeholder="Buscar" v-model="searchQuery">
<input class="neo-form-toggle__field" :id="selectAllRules" #click="selectAllRules($event)" type="checkbox"/>
<label class="neo-form-toggle__label" :for="selectAllRules">selecionar tudo</label>
</div>
</div>
</div>
<div slot="content">
<div v-for="policyRule in filteredPolicyRules.group" :key="policyRule.name"
class="neo-form-group">
<li v-text="policyRule.name"></li>
<div class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="policyRule.name" #click="selectGroupRules(policyRule.rule, policyRule.name, $event)" type="checkbox" v-model="policyRules.name" />
<label class="neo-form-toggle__label" :for="policyRule.name">selecionar grupo</label>
</div>
<div class="neo-form-toggle-list neo-form-toggle-list--inline">
<div v-for="rule in policyRule.rule" :key="rule.title"
class="neo-form-toggle-list__item neo-form-toggle neo-form-toggle--checkbox">
<input class="neo-form-toggle__field" :id="rule.title" :value="rule" name="rule" type="checkbox" v-model="checkedRules"/>
<label class="neo-form-toggle__label" :for="rule.title">{{ rule.title }}</label>
<h6 class="neo-text-disabled-options">{{ rule.description }}</h6>
</div>
</div>
</div>
</div>
<div slot="footer">
<span>{{ checkedRules }}</span>
</div>
</slide-out>
</div>
</template>
<script>
import { mapState } from 'vuex';
import SlideOut from '#/components/shared/slideout/SlideOut.vue';
export default {
name: 'ModulePolicyRules',
props: [],
components: {
SlideOut,
},
data() {
return {
isVisible: false,
policyRules: [],
searchQuery: '',
checkedRules: [],
filteredRules: [],
};
},
computed: {
filteredPolicyRules() {
const me = this;
if (this.searchQuery) {
me.filteredRules.pop();
this.policyRules.group.filter((ruleGroup) => {
ruleGroup.rule.forEach((rule) => {
if (rule.title.startsWith(this.searchQuery)) {
console.log(me.filteredRules);
me.filteredRules.push(rule);
}
});
});
console.log(me.filteredRules);
return me.filteredRules;
// return this.policyRules.group.filter(item => item.name.startsWith(this.searchQuery));
}
return this.policyRules;
},
},
methods: {
async loadData() {
const rules = await this.$store.dispatch('policyrules/setPolicyRules');
this.policyRules = rules;
},
},
mounted() {
this.loadData();
},
};
</script>
<style lang="scss">
.neo-form-toggle__label {
text-transform: none;
font-weight: 600;
}
.neo-text-disabled-options {
text-transform: none;
}
</style>
Object expected result using "Rec" in search field:
{
"name": "Cadastral",
"rule": [
{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
Try this computed prop.
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = [];
rule.forEach(r => {
if (r.title.startsWith(this.searchQuery)) {
rules.push(r);
}
});
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
I'd suggest calling them groups and rules (plural) respectively, to avoid future confusion -- after all they are arrays.
Full demo:
const policyRules = {
"id": "rulesCompany",
"group": [{
"name": "Cadastral",
"rule": [{
"title": "Receita Federal",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "CNAE Primário - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
]
},
{
"name": "Dados modelados",
"rule": [{
"title": "Nível de Atividade - Alteração",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "high"
},
{
"title": "Faturamento Presumido",
"description": "Fonte atualizada mensalmente.",
"homonym": false,
"criticality": "medium"
}
]
}]
};
new Vue({
el: '#app',
data() {
return {
searchQuery: '',
policyRules
}
},
computed: {
filteredPolicyRules() {
if (this.searchQuery) {
return this.policyRules.group.reduce((groups, { name, rule }) => {
const rules = rule.filter(this.matchFilter);
if (rules.length > 0) {
groups.push({
name,
rules
});
}
return groups;
}, []);
}
return this.policyRules;
}
},
methods: {
matchFilter(item) {
const
search = this.searchQuery.toLowerCase(),
term = (item.title || '').toLowerCase();
return term.includes(search);
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input v-model="searchQuery" placeholder="Search..." />
<pre>{{ filteredPolicyRules }}</pre>
</div>
So first I've put your list into a map variable. Then I'm filtering that by checking if any of the wanted properties contain the search term. The array.filter Method returns a new array based on what entries returned true and what false.
I'm checking inside name, title and description. Also I made everything lower case so case doesn't matter.
Array.prototype.filter Reference
const term = 'CNE';
console.log(map.filter(e => {
const { name } = e;
if(contains(name, term)) return true;
for(const _r of e.rule) {
const { title, description } = _r;
if(contains(title, term)) return true;
if(contains(description, term)) return true;
}
return false;
}));
function contains(str, term) {
return str.toLowerCase().includes(term.toLowerCase());
}
And I would also suggest like Yom in his answer that you use groups and rules so you can name them better. So that then would be groups.filter(group => {[..]}) and for(const rule of group.rules)
I have a scrolling menu items, and the titles of each item is hardcoded into a const, along side with the id
const list = [
{ name: "category1", id: 0 },
{ name: "category2", id: 1 },
{ name: "category3", id: 2 },
{ name: "category4", id: 3 },
{ name: "category5", id: 4 },
{ name: "category6", id: 5 },
{ name: "category7", id: 6 },
{ name: "category8", id: 7 }
];
I have a json file that contains the category name for each child:
{
"results": [
{
"category": "category1",
"name": {
"title": "mr",
"first": "ernesto",
"last": "roman"
},
"email": "ernesto.roman#example.com",
"id": {
"name": "DNI",
"value": "73164596-W"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/73.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/73.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/73.jpg"
}
},
{
"category": "category2",
"name": {
"title": "mr",
"first": "adalbert",
"last": "bausch"
},
"email": "adalbert.bausch#example.com",
"id": {
"name": "",
"value": null
} etc....
I want to show these categories "category": "category1", as the titles of my menu, I now that I need to start stateless and add them from the JSON, the fetching part from the JSON is done locally in componentDidMount, but I am not sure how can I map them into appearing as menu names to make the menu dynamic, I basically want the same output but from the json not hardcoded. here is a sandbox snippet, would appreciate the help.
https://codesandbox.io/s/2prw4j729p?fontsize=14&moduleview=1
Just convert the JSON output to an object like list with a map function from the results and then set is as MenuItems on the state, which is what you pass to the function on render(). Like that.
import React, { Component } from "react";
import ScrollMenu from "react-horizontal-scrolling-menu";
import "./menu.css";
// One item component
// selected prop will be passed
const MenuItem = ({ text, selected }) => {
return (
<div>
<div className="menu-item">{text}</div>
</div>
);
};
// All items component
// Important! add unique key
export const Menu = list =>
list.map(el => {
const { name, id } = el;
return <MenuItem text={name} key={id} />;
});
const Arrow = ({ text, className }) => {
return <div className={className}>{text}</div>;
};
export class Menucat extends Component {
state = {
selected: "0",
MenuItems: []
};
componentDidMount() {
fetch("menu.json")
.then(res => res.json())
.then(result => {
const items = result.results.map((el, idx) => {
return { name: el.category, id: idx };
});
this.setState({
isLoaded: true,
MenuItems: items
});
});
}
render() {
const { selected, MenuItems } = this.state;
// Create menu from items
const menu = Menu(MenuItems, selected);
return (
<div className="App">
<ScrollMenu
data={menu}
selected={selected}
onSelect={this.onSelect}
alignCenter={true}
tabindex="0"
/>
</div>
);
}
}
export default Menucat;
Cheers!
Looks like you don't have to hard code your category list at all. In your componentDidMount() fetch the json and group the results into separate categories like this:
const json = {
"results": [
{
category: "category1",
name: "Fred"
},
{
category: "category1",
name: "Teddy"
},
{
category: "category2",
name: "Gilbert"
},
{
category: "category3",
name: "Foxy"
},
]
}
const grouped = json.results.reduce((acc, cur) => {
if (!acc.hasOwnProperty(cur.category)) {
acc[cur.category] = []
}
acc[cur.category].push(cur)
return acc;
}, { })
// parent object now has 3 properties, namely category1, category2 and category3
console.log(JSON.stringify(grouped, null, 4))
// each of these properties is an array of bjects of same category
console.log(JSON.stringify(grouped.category1, null, 4))
console.log(JSON.stringify(grouped.category2, null, 4))
console.log(JSON.stringify(grouped.category3, null, 4))
Note that this json has 4 objects in result array, 2 of cat1, and 1 of cat 2 and cat3. You can run this code in a separate file to see how it works. Ofcourse you will be fetching the json object from server. I just set it for demonstration.
Then set teh state:
this.setState({ grouped })
Then in render() you only show the categories that have items like:
const menuBarButtons = Object.keys(this.state.grouped).map((category) => {
/* your jsx here */
return <MenuItem text={category} key={category} onClick={this.onClick} blah={blah}/>
/* or something , it's up to you */
})
I'm assuming you're showing the items based on the currently selected category this.state.selected. So after you have rendered your menu, you would do something like:
const selectedCatItems = this.state.grouped[this.state.selected].map((item) => {
return <YourItem name={item.name} key={item.id} blah={blah} />
})
Then render it:
return (
<div className="app">
<MenuBar blah={blah}>
{menuBarButtons}
</Menubar>
<div for your item showing area>
{selectedCatItems}
</div>
</div>
)
Also, don't forget to change your onClick() so that it sets this.state.selected state properly. I believe you can figure that out yourself.
Hope it helps.
PS: I didn't write a whole copy/paste solution to your problem simply because I'm reluctant to read and understand your UI details and the whole component to component data passing details..