How to map twice using Gatsby? - javascript

I want to create a table similar to how it's shown here ("Paslaugos" section), and allow the client to change table content using WordPress.
So I looped through, and displayed images and titles, without issue. However, I can't figure it out how to display table items. How do you map items twice in this context?
Update
{node.acf.table_items.header.map(({ c }) => (
<span>{c}</span>
))}
{node.acf.table_items.body[0].map(({ c }) => (
<span>{c}</span>
))}
So I kinda figure it out. This way would display header, but display only first item in the table. I need to loop body[0] in order to work, however I can't figure it out the exact syntax.
So thanks to ZeroToMastery forum the correct answer would be this:
{node.acf.table_items.body.map(mappingBody => {
return mappingBody.map(({ c, index }) => {
return (
<span key={index} className={classes.body}>
{c}
</span>
)
})
})}
Current Result
<StaticQuery
query={graphql`
{
allWordpressPost(
filter: {
categories: { elemMatch: { name: { eq: "favours" } } }
}
) {
edges {
node {
id
acf {
favours_title
table_items {
body {
c
}
header {
c
}
}
favours_images {
localFile {
childImageSharp {
fluid(maxWidth: 600) {
...GatsbyImageSharpFluid
}
}
}
}
}
}
}
}
}
`}
render={data =>
data.allWordpressPost.edges.map(({ node }) => (
<div key={node.id} className={classes.item}>
<Img
className={classes.img}
fluid={
node.acf.favours_images.localFile.childImageSharp.fluid
}
/>
<h2 className={classes.title}>{node.acf.favours_title}</h2>
<div className={classes.grid}>
{node.acf.table_items.header.map(({ header }) => (
<span>{header}</span>
))}
{node.acf.table_items.body.map(({ body }) => (
<span>{body}</span>
))}
</div>
</div>
))
}
/>

I'm presuming that the data in body is a 2D nested array, e.g.
node.acf.table_items.body = [['row1-columnA', 'row1-columnB'], ['row2-columnA', 'row2-columnB']];
To map through each item in a 2D array, you could use map() twice:
{node.acf.table_items.body.map(c => c.map(el => (
<span>{el}</span>
)))}
Or, if you know there are a set number of items in each sub-array, you could use target the specific elements using square bracket notation:
{node.acf.table_items.body.map(c => (
<span>{c[0]}</span>
<span>{c[1]}</span>
))}

So thanks to Zero To Mastery forum, the correct answer would be this:
{node.acf.table_items.body.map(mappingBody => {
return mappingBody.map(({ c, index }) => {
return (
<span key={index} className={classes.body}>
{c}
</span>
)
})
})}

Related

I want to be able to toggle between true or false within the same array

I currently have two lists (previewList and activeList)
previewList is the bottom half (blue) list that will show all the clients if there is no other clients registered.
activeList is the top half (grayed) list that will show all the clients that have the same name that already are registered.
basically when i click "Move client to the list" i just want to put that client into the bottom half list (previewList) and also get rid of it from its current place.
Below is how i render out my lists:
<S.ListWrapper>
<div
style={{
overflowY: setScroll,
height: "440px",
padding: "2rem",
border: "1px solid #B2CAE060",
borderRadius: "2rem",
marginBottom: "2rem",
}}
>
{showAll && (
<div>
<S.SectionName>Missmatch on org name or org number</S.SectionName>
{activeList.map((client, index) => {
return (
<MatchResult
clientType={clientType}
client={client}
key={index}
activePid={activePid}
updateItem={updateItem}
isChecked={selectedClients[index]}
setIsChecked={setIsChecked(index)}
index={index} // here we have the right index so this is ok
handleGcidGenerate={handleGcidGenerate}
/>
);
})}
<S.SectionName></S.SectionName>
</div>
)}
{showAll
? previewList.map((client, index) => {
return (
<MultipleClientsItem
clientType={clientType}
client={client}
key={index}
activePid={activePid}
updateItem={updateItem}
isChecked={selectedClients[index]}
setIsChecked={setIsChecked(index)}
index={index} // here we have the right index so this is ok
handleGcidGenerate={handleGcidGenerate}
/>
);
})
: previewList.reduce((array, client, index) => {
if (array.length >= 3) return array;
if (
client.client_type &&
client.client_type.includes(clientType)
) {
array.push(
<MultipleClientsItem
clientType={clientType}
client={client}
key={index}
activePid={activePid}
updateItem={updateItem}
isChecked={selectedClients[index]}
setIsChecked={setIsChecked(index)}
index={index}
handleGcidGenerate={handleGcidGenerate}
/>
);
}
return array;
}, [])}
</div>
</S.ListWrapper>
And this is my how i currently trying to solve my problem:
Where "clients" is the already registered clients
useEffect(() => {
let filtered = previewList.filter((o1) =>
clients.some((o2) => o1.org_name === o2.org_name)
);
let filteredTrue = previewList.filter((o1) =>
clients.every((o2) => o2.org_name !== o1.org_name)
);
const is_same = previewList.filter((o1) =>
clients.every((o2) => o2.org_name !== o1.org_name)
);
console.log(is_same);
setActiveList(
previewList.map((client) => {
if (is_same) {
return { ...client, isActive: false };
}
})
);
dispatch(
setPreviewList(
filteredTrue.map((client) => {
return { ...client, isActive: true };
})
)
);
}, [clients]);
What i am trying to achive here is that if org_name from previewList find a match with org_name from clients then i want to set isActive: True otherwise/or rest isActive: False
I think this is attainable with just one list aswell, but i cant find any answers to this solution anywhere.
So i can basically move the client through a toggle

React component returns empty doing foreach on JSON object

I have a JSON object with multiple leves that I'm trying to iterate through to display like this, but I keep getting empty results.
<div>
Operation
**Speed: Slow
**Direction: Forward
**Status: Moving
</div>
<div>
Detail
**Type: Electric
**Drivetrain: AWD
</div>
JSON object
"operation": {
"speed": "slow",
"direction": "forward",
"status": "moving"
},
"detail": {
"type": "electric",
"drivetrain": "AWD"
}
The following works, but difficult to wrap each level in a DIV
const Bompush = (obj) => {
let output = []
Object.keys(obj).forEach(key => {
output.push(<div key={key}>{key}</div>)
Object.keys(obj[key]).forEach (value => {
output.push(<div key={value}>{value}</div>)
})
})
return output
}
This is the ideal way I'd like to do this but it returns empty
const Bom = (obj) => {
return (
<div className="outerContainer">
{
Object.keys(obj).forEach((key, item) => {
return (
<div className="category" key={key}>{key}
{
Object.keys(obj[key]).forEach (value => {
return (<div className="item" key={value}>{value}</div>);
})
}
</div>
);
})
}
</div>
);
}
My React component
export default class DiplayBom extends React.Component {
render() {
return (
Bom(this.props.myValues)
)
}
}
forEach doesn't collect the results. You want map:
Object.keys(obj[key]).map(value => (
<div className="item" key={value}>{value}</div>
))
You'd be better off pulling out the inner component (at the very least), because as simple as this is, it's already pretty hard to read. I'd consider restructuring the object, plus you're at the mercy of object property ordering, so you may not always get the results you'd prefer.
There's some refactoring and/or rethinking to be done.
Without deviating much from your original code, and completely untested, I'd probably start thinking more along these lines:
const Item = ({ value, item }) => (
<div className="item">{value}: {item}</div>
)
const Category = ({ cat, obj }) => {
return (
<div className="category">
{key}
{Object.keys(obj).map(val => <Item key={val} value={val} item={obj[val]} />)}
</div>
)
}
const Bom = obj => (
<div className="outerContainer">
{Object.keys(obj).map(cat => <Category key={cat} cat={cat} obj={obj[cat]} />)}
</div>
)

Nested If and map use in reactjs?

This is my following code:-
if(this.props.buttons && this.props.buttons.tabs && this.props.buttons.tabs.length) {
this.props.buttons.tabs.map((button) => {
return (
<TabsPanel label={button.labelKey} />
button.subtabs.map((subtab) => {
return(
<Tabs>
<TabsPanel label={subtab.labelKey}></TabsPanel>
</Tabs>
)
})
)
});
}
While running this following code it's always giving Syntax Error.
Here's my following JSON which I am getting
"tabs" : [
{
"labelKey" : "label1",
"subtabs" : [
{
"form" : {
"labelKey" : "subtab1"
}
},
{
"form" : {
"labelKey" : "subtab2"
}
}
]
},
{
"labelKey" : "label2"
}
]
Any leads would be helpful. I am stuck at a dead end right now.
Thanks!
The question is not very clear. But here are a few scenarios that I think could be intended.
TabsPanel component accepts Tabs as children:
Answer by Prabin is as correct as it gets. (Except for missing key attributes in TabsPanel and Tabs components)
TabsPanel component and Tabs list go side by side:
Note: A valid component is either one single root, or it is a list of other valid components.
So either this is correct.
this.props.buttons.tabs.map((button, index) => {
return (
<div key={index}>
<TabsPanel label={button.labelKey} />
{button.subtabs.map(subtab => {
return (
<Tabs key={subtab.labelKey}>
<TabsPanel label={subtab.labelKey} />
</Tabs>
)
})}
</div>
)
})
Or this is correct.
this.props.buttons.tabs.map((button, index) => {
return [
<TabsPanel label={button.labelKey} key={index} />,
{
...button.subtabs.map(subtab => {
return (
<Tabs key={subtab.labelkey}>
<TabsPanel label={subtab.labelKey} />
</Tabs>
)
})
}
]
})
Also, note the key attribute in Tabs and TabsPanel components. That is important and I leave it up to you to find it's importance.
return (
<div>
{this.props.buttons && this.props.buttons.tabs && this.props.buttons.tabs.length &&
this.props.buttons.tabs.map(button => {
return (
<TabsPanel label={button.labelKey}>
{button.subtabs.map(subtab => {
return (
<Tabs>
<TabsPanel label={subtab.labelKey} />
</Tabs>
);
})}
</TabsPanel>
);
})}
</div>
);
Try this should solve your problem

Pass index as state to component using React

I have 4 different divs each containing their own button. When clicking on a button the div calls a function and currently sets the state to show a modal. Problem I am running into is passing in the index of the button clicked.
In the code below I need to be able to say "image0" or "image1" depending on the index of the button I am clicking
JS:
handleSort(value) {
console.log(value);
this.setState(prevState => ({ childVisible: !prevState.childVisible }));
}
const Features = Array(4).fill("").map((a, p) => {
return (
<button key={ p } onClick={ () => this.handleSort(p) }></button>
)
});
{ posts.map(({ node: post }) => (
this.state.childVisible ? <Modal key={ post.id } data={ post.frontmatter.main.image1.image } /> : null
))
}
I would suggest:
saving the button index into state and then
using a dynamic key (e.g. object['dynamic' + 'key']) to pick the correct key out of post.frontmatter.main.image1.image
-
class TheButtons extends React.Component {
handleSort(value) {
this.setState({selectedIndex: value, /* add your other state here too! */});
}
render() {
return (
<div className="root">
<div className="buttons">
Array(4).fill("").map((_, i) => <button key={i} onClick={() => handleSort(i)} />)
</div>
<div>
posts.map(({ node: post }) => (this.state.childVisible
? <Modal
key={ post.id }
data={ post.frontmatter.main.[`image${this.state.selectedIndex}`].image }
/>
: null
))
</div>
</div>
);
}
}
This is a good answer which explains "Dynamically access object property using variable": https://stackoverflow.com/a/4244912/5776910

Render a map of div and their title

I would like to simplify "renderTitle" and "renderComments" in a unique function in a React component:
renderTitle(dish) {
return (
<h2>
Title array comment
</h2>
);
}
renderComments(dish) {
return (
dish.array.map(comment => {
return (
<div>
hello
</div>
);
})
);
}
render() {
return (
{this.renderTitle(this.props.dish)}
{this.renderComments(this.props.dish)}
);
}
Take a look at below code where I used Fragment (react 16.x). And see how I merged the functions in your question.
renderItems(dish) {
const comments = dish.array.map(comment => (<div>hello</div>))
return (
<React.Fragment>
<h2>
Title array comment
</h2>
{comments}
</React.Fragment>
);}
To use regular JavaScript expressions in the middle of JSX you have to wrap the JavaScript expression in {}.
Note that to return multiple elements on the same level you should either return an array or wrap them in a Fragment:
render() {
return (
<React.Fragment>
<h2>
Title array comment
</h2>
{
dish.array.map(comment => (
<div>
hello
</div>
));
}
</React.Fragment>
);
}
You can do this if this.props.dish is array.
renderTitle(dish) {
return dish.map(i => {
return <div>{i.title}</div>;/*this title may change*/
});
}
renderComments(dish) {
return dish.map(i => {
return <div>{i.comment}</div>;/*this comment may change*/
});
}
render() {
return (
<div>
{this.renderTitle(this.props.dish)}
{this.renderComments(this.props.dish)}
</div>
);
}

Categories