I am writing some code for a ReactJS component to have an array of chips. I want each chip to be styled uniquely, so I set up a makeStyles class for each one. I was having trouble trying to figure out how to change the class for each tag. This is what I got so far:
const classes = useStyles();
const [chipData, setChipData] = React.useState([
{ key: 0, label: 'Heating' },
{ key: 1, label: 'Printing' },
{ key: 2, label: 'Resetting' },
{ key: 3, label: 'Idle' },
{ key: 4, label: 'Suspended' },
{ key: 5, label: 'Suspend in Progress' },
{ key: 6, label: 'Attention - Printer Connection Lost' },
{ key: 7, label: 'Attention - Filament Out' },
{ key: 8, label: 'Attention - Cooldown Failed' },
]);
return (
<Box display="flex" flexDirection="row" alignItems="flex-start" className={classes.container}>
{chipData.map((data) => {
return (
<div classes={classes.chipContainer}>
<li key={data.key}>
<Chip
label={data.label}
if (label === 'Heating') {
className={classes.heatingTag}
}
/>
</li>
</div>
);
})}
</Box>
);
}
export default PrinterStatusTags
So within the chip element, I have an if statement that is used to assign a specific class based on the label. My plan was to have an if statement for each label, but I am getting the following error:
Parsing error: Unexpected token
Any ideas how I can assign a class based on the chip?
Updated Answer
I would 2 things:
Add a new type property for every chip.
Create a mapper from the type (in the 1st point) to the classes
const classesMapper = {
first: classes.firstClass,
second: classes.secondClass
// ...
};
const [chipData, setChipData] = React.useState([
{ key: 0, label: 'Heating', type: 'first' },
{ key: 1, label: 'Printing', type: 'seocnd' },
// ....
]);
After you have the mapping between every chip to its type. Just render it:
return (
<Box display="flex" flexDirection="row" alignItems="flex-start" className={classes.container}>
{chipData.map((data) => {
return (
<div classes={classes.chipContainer}>
<li key={data.key}>
<Chip
label={data.label}
className={classesMapper[data.type]} />
</li>
</div>
);
})}
</Box>
);
Old Answer
You should write the code a little bit different:
Use className property and not the property class (nor classes)
Set the condition inside the className property. Please note that there are better ways to set the right class but for your case, that would be good enough.
This is the code as it should be:
<div classeName={classes.chipContainer}>
<li key={data.key}>
<Chip label={data.label} className={ label ==== 'Heating' && classes.heatingTag}/>
</li>
</div>
Related
I have a .js file containing the code for a context menĂ¹ component:
const ContextMenuDialog = (props) => {
// my state declaration, other const, etc...
const build_ITEMS_ContextMenu = () => {
const A = [
{
key: "0",
label: "AA123BB",
disabled: true
},
{
key: "1",
label: "Show"
},
{
key: "2",
label: "Edit"
},
{
key: "3",
label: "Save"
}
];
return A;
};
return (
<div>
{loading ? (
"Loading"
) : (
<Menu
title="Menu right click"
style={{ top: 10, left: 10 }}
onClick={my_onClick_function}
items={ build_ITEMS_ContextMenu }
/>
)}
</div>
)
}
export default ContextMenuDialog;
Just consider that I cannot simply past the code of const A directly inside the "items" element; if I do it,the code works properly. In the real life I need to build the const A with a my algorithm.
This code doesn't work, the context menĂ¹ is not shown!
How can I solve this problem?
the problem is that you are not calling the function.
try this items={ build_ITEMS_ContextMenu() }
This question already has answers here:
How to Render Nested Map Items inside react component
(2 answers)
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 8 months ago.
So, I have a file data.js where there is an array with some navigation items.
export const footerNavigation = [
{
category: 'Resources',
items: [
{
href: '',
text: 'Guides'
},
{
href: '',
text: 'Blog'
},
{
href: '',
text: 'Customer stories'
},
{
href: '',
text: 'Glossery'
}
]
},
{
category: 'Resources',
items: [
{
href: '',
text: 'Guides'
},
{
href: '',
text: 'Blog'
},
{
href: '',
text: 'Customer stories'
},
{
href: '',
text: 'Glossery'
}
]
},
];
Footer.jsx
import React from 'react';
import { footerNavigation } from '../data/data';
const Footer = () => {
return (
<div>
{footerNavigation.map((item, index) => {
return (
<div key={index}>
<h3 className='text-lg font-bold mb-8'>{item.category}</h3>
<ul>
<li>
</li>
</ul>
</div>
)
})}
</div>
)
}
export default Footer;
The task is to make sure there is an href value inside a link tag like <a href={.href}</a> and the value of the item here <a>{.value}</a>
I have a basic understanding how to map items that are in the initial array, but have no clue how to map the array that is inside an object which is inside the initial array.
You only missing another items iteration:
const Footer = () => {
return (
<div>
{/* category unique acts as key */}
{footerNavigation.map(({ category, items }) => {
return (
<div key={category}>
<h3 className="text-lg font-bold mb-8">{category}</h3>
<ul>
{/* text unique should be a key */}
{items.map(({ href, text }) => (
<a key={text} href={href}>
{text}
</a>
))}
</ul>
</div>
);
})}
</div>
);
};
Im trying to make a navigation bar for a website and it's giving me the "Warning: Each child in a list should have a unique "key" prop." inside my props.dropList.map
I have two files:
NavigationItems.js -> where I render my navigation bar
const NavigationItems = () => {
const projectDropdown = [
{ id: 0, value: "architecture" },
{ id: 1, value: "land" },
{ id: 2, value: "design" },
{ id: 3, value: "list" },
];
const officeDropdown = [
{ id: 4, value: "contact" },
{ id: 5, value: "team" },
];
return (
<div>
<ul className={styles.NavigationItems}>
<NavigationItem
link={`/projects`}
name="projects"
dropList={projectDropdown}
/>
<NavigationItem link={`/news`} name="news" exact />
<NavigationItem
link={`/office`}
name="office"
dropList={officeDropdown}
/>
</ul>
</div>
);
};
export default NavigationItems;
NavigationItem.js -> where I use the map function
const NavigationItem = (props) => {
let i = 0;
return (
<li className={styles.NavigationItem}>
<NavLink to={props.link} activeClassName={styles.active}>
{props.name}
</NavLink>
{props.dropList && (
<div className={styles.DropdownItems}>
<ul className={styles.DropdownItem}>
{props.dropList.map((drop) => {
console.log("i " + i);
console.log("id " + drop.id);
console.log("value " + drop.value);
i++;
return (
<li key={drop.id}>
<NavLink
exact
to={`${props.link}/${drop.value}`}
activeClassName={styles.active}
>
{drop.value}
</NavLink>
</li>
);
})}
</ul>
</div>
)}
</li>
);
};
export default NavigationItem;
So what happens is that the code loops twice duplicating the key values. It should be looping only once. I don't know why it loops twice, I'm only mapping my values once. For reference
this is what my console shows when I click my links
So your problem doesn't occure in either of the components you provided, but in your "Land" component. (Check the render method of Land)
I am trying to use JsonSchema-Form component but i ran into a problem while trying to create a form that, after choosing one of the options in the first dropdown a secondary dropdown should appear and give him the user a different set o options to choose depending on what he chose in the first dropdown trough an API call.
The thing is, after reading the documentation and some examples found here and here respectively i still don't know exactly how reference whatever i chose in the first option to affect the second dropdown. Here is an example of what i have right now:
Jsons information that are supposed to be shown in the first and second dropdowns trough api calls:
Groups: [
{id: 1,
name: Group1}
{id: 2,
name: Group2}
]
User: [User1.1,User1.2,User2.1,User2.2,User3.1,User3.2, ....]
If the user selects group one then i must use the following api call to get the user types, which gets me the the USER json.
Component That calls JSonChemaForm
render(){
return(
<JsonSchemaForm
schema={someSchema(GroupOptions)}
formData={this.state.formData}
onChange={{}}
uiSchema={someUiSchema()}
onError={() => {}}
showErrorList={false}
noHtml5Validate
liveValidate
>
)
}
SchemaFile content:
export const someSchema = GroupOptions => ({
type: 'object',
required: [
'groups', 'users',
],
properties: {
groups: {
title: 'Group',
enum: GroupOptions.map(i=> i.id),
enumNames: GroupOptions.map(n => n.name),
},
users: {
title: 'Type',
enum: [],
enumNames: [],
},
},
});
export const someUISchema = () => ({
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
},
types: {
'ui:options': {
size: {
lg: 15,
},
},
},
});
I am not really sure how to proceed with this and hwo to use the Onchange method to do what i want.
I find a solution for your problem.There is a similar demo that can solve it in react-jsonschema-form-layout.
1. define the LayoutField,this is part of the demo in react-jsonschema-form-layout.To make it easier for you,I post the code here.
Create the layoutField.js.:
import React from 'react'
import ObjectField from 'react-jsonschema-form/lib/components/fields/ObjectField'
import { retrieveSchema } from 'react-jsonschema-form/lib/utils'
import { Col } from 'react-bootstrap'
export default class GridField extends ObjectField {
state = { firstName: 'hasldf' }
render() {
const {
uiSchema,
errorSchema,
idSchema,
required,
disabled,
readonly,
onBlur,
formData
} = this.props
const { definitions, fields, formContext } = this.props.registry
const { SchemaField, TitleField, DescriptionField } = fields
const schema = retrieveSchema(this.props.schema, definitions)
const title = (schema.title === undefined) ? '' : schema.title
const layout = uiSchema['ui:layout']
return (
<fieldset>
{title ? <TitleField
id={`${idSchema.$id}__title`}
title={title}
required={required}
formContext={formContext}/> : null}
{schema.description ?
<DescriptionField
id={`${idSchema.$id}__description`}
description={schema.description}
formContext={formContext}/> : null}
{
layout.map((row, index) => {
return (
<div className="row" key={index}>
{
Object.keys(row).map((name, index) => {
const { doShow, ...rowProps } = row[name]
let style = {}
if (doShow && !doShow({ formData })) {
style = { display: 'none' }
}
if (schema.properties[name]) {
return (
<Col {...rowProps} key={index} style={style}>
<SchemaField
name={name}
required={this.isRequired(name)}
schema={schema.properties[name]}
uiSchema={uiSchema[name]}
errorSchema={errorSchema[name]}
idSchema={idSchema[name]}
formData={formData[name]}
onChange={this.onPropertyChange(name)}
onBlur={onBlur}
registry={this.props.registry}
disabled={disabled}
readonly={readonly}/>
</Col>
)
} else {
const { render, ...rowProps } = row[name]
let UIComponent = () => null
if (render) {
UIComponent = render
}
return (
<Col {...rowProps} key={index} style={style}>
<UIComponent
name={name}
formData={formData}
errorSchema={errorSchema}
uiSchema={uiSchema}
schema={schema}
registry={this.props.registry}
/>
</Col>
)
}
})
}
</div>
)
})
}</fieldset>
)
}
}
in the file, you can define doShow property to define whether to show another component.
Next.Define the isFilled function in JsonChemaForm
const isFilled = (fieldName) => ({ formData }) => (formData[fieldName] && formData[fieldName].length) ? true : false
Third,after you choose the first dropdown ,the second dropdown will show up
import LayoutField from './layoutField.js'
const fields={
layout: LayoutField
}
const uiSchema={
"ui:field": 'layout',
'ui:layout': [
{
groups: {
'ui:autofocus': true,
'ui:options': {
size: {
lg: 15,
},
},
}
},
{
users: {
'ui:options': {
size: {
lg: 15,
},
},
doShow: isFilled('groups')
}
}
]
}
...
render() {
return (
<div>
<Form
schema={schema}
uiSchema={uiSchema}
fields={fields}
/>
</div>
)
}
I'm still getting to grips with react but I can't see why this isn't working, it should be passing the props from tabs into <Tab /> and outputting the button each time.
If I put no text next to {this.props.content} it doesn't display anything, if I put testText next to {this.props.content} it will output the button 5 times but only display testText not the name field it should be displaying via the content={item.name} prop
class TopCategories extends React.Component {
render() {
const Tab = () => (
<TestBtn key={this.props.key} >
testText {this.props.content}
</TestBtn>
)
const items = [
{ id: 1, name: 'tab-1', text: 'text' },
{ id: 2, name: 'tab-2', text: 'text' },
{ id: 3, name: 'tab-3', text: 'text' },
{ id: 4, name: 'tab-4', text: 'text' },
{ id: 5, name: 'tab-5', text: 'text' },
]
const tabs = items.map(item =>
<Tab key={item.id} content={item.name} />,
)
return (
<Container>
<Wrapper>
{tabs}
</Wrapper>
</Container>
)
}
}
export default TopCategories
You need to pass props to the stateless function and since it's a stateless component, this is not available. It should be something like:
const Tab = (props) => {
return (
<TestBtn key={props.key} >
testText {props.content}
</TestBtn>
);
}