React.js how to send array to react router - javascript

I have an array like this :
const types = [
{
name: 'Catalog A',
slug: 'catalog-a'
},
{
name: 'Catalog B',
slug: 'catalog-b'
},
{
name: 'Catalog C',
slug: 'catalog-c'
},
{
name: 'Catalog D',
slug: 'catalog-d'
},
]
this array is mapped and made into buttons or Links for react-router... the Links works, but not my problem is that I want to pass these array to the components that have no connection from the above components.
here is the mapped button :
{types.map(type => (
<ButtonToggle
key={type}
active={active === type}
onClick={() => setActive(type)}
show={parseFloat(show)}
>
<Link to={type.slug} >
{type.name}
<p>
Unpublished
</p>
</Link>
</ButtonToggle>
))}
here is what I have in mind for mapping the route :
{types.map(type => (
<Route
path={type.slug}
>
<h1>{type.name}</h1>
</Route>
))}
but now types array must be sent to the above code/components...
I am done with the array, I just make a separate file for it, but i the active state is not done yet.
active={active === type}

You can try this way:
<Link to={{ pathname: type.slug, state: someData }} >
then in your next location (type.slug I suppose) you should have access like this:
this.props.location.state

Related

Next.JS router.query returns undefined for arrays

I'm trying to pass objects of an array in another page using router.query, single objects such as router.query.title work fine, but when it comes to arrays such as router.query.reviews it returns something like this reviews: ['', '', '', '', '', '', '', '']. I've tried using router.isReady but nothing seems to change
Example object in the array
{
title: "Center-Hall",
reviews: [
{
title: "Wislong Chew",
comment: "jsdncjnsd csdncjds csjcnsdj csjdcn cdc djndc",
},
{
title: "Wisdsdlong Chew",
comment: "jsdncjnsd csdncjds csjcnsdj csjdcn cdc djndc",
},
],
},
Parent
{array.map((item) => (
<Grid key={item} item>
<Card
label={item.title}
onClick={() =>
router.push({
pathname: "/page",
query: {
title: item.title,
reviews: item.reviews,
},
})
}
/>
</Grid>
))}
Next Page
function index() {
const router = useRouter();
useEffect(() => {
if (!router.isReady) return;
console.log(router.query);
}, [router.isReady]);
const Reviews = () =>
<ReviewCard
reviewsList={router.query.reviews}
/>
);
return (
<Box>
<Typography>{router.query.title}</Typography>
<Reviews />
</Box>
);
}
export default index;
When you passing an array using router.query to another page parse it into a json string using the JSON.stringify method and on the next page parse the string into an array using the JSON.parse method
Parent
{array.map((item) => (
<Grid key={item} item>
<Card
label={item.title}
onClick={() =>
router.push({
pathname: "/page",
query: {
title: item.title,
reviews: JSON.stringify(item.reviews),
},
})
}
/>
</Grid>
))}
Next Page
function index() {
const router = useRouter();
useEffect(() => {
if (!router.isReady) return;
console.log(router.query);
console.log(JSON.parse(router.query.reviews))
}, [router.isReady]);
const Reviews = () =>
<ReviewCard
reviewsList={JSON.parse(router.query.reviews)}
/>
);
return (
<Box>
<Typography>{router.query.title}</Typography>
<Reviews />
</Box>
);
}
export default index;
It happens when the array item is a JSON object.You can pass array but not array of objects.
router.push({
pathname: "/planterupdate",
query: {
title: "test",
textArray: ["1","2","3","4"],//will work
reviews: [{num:1},{num:2},{num:3},{num:4}],//will not work
numArray: [5,6,7,8],//will work
},
})
Sample log of query
{title: 'test', textArray: Array(4), reviews: Array(4), numArray: Array(4)}
numArray: (4) ['5', '6', '7', '8']
reviews: (4) ['', '', '', '']
textArray: (4) ['1', '2', '3', '4']
title: "test"

Count number of Children in recursive react component

I'm working on a comment system and used a recursive approach to show parent and child comments. When I delete a child, I want to update the count of child comments for its parent.
//json data
data = [{
name: 'Parent1',
childComments: [{
name: 'Child1',
text:'child comment1'
},
{
name: 'Child2',
text:'child comment2'
}]
},{
name: 'Parent2',
childComments: [{
name: 'Child1',
text:'child comment1'
},
{
name: 'Child2',
text:'child comment2'
}]
}]
const Comment = ({ item }) => {
const childComment = item
//delete child element and update the parent count
const deleteChildComment =(item) => {
}
return childComment && (
<>
{name}
Children count : {childComments.length}
<Button
className={styles.replies}
onClick={() => {
setNewCommentAdded(false)
deleteChildComment(childComment)
}}
> Delete This comment </Button>
{childComments && items.map((item) => (
<Comment item={item} />
))}
</>
)
}
Counting the children would be a bad idea and make things way too complicated.
Just use the data you use to generate the comment component.
As a rule, always make a UI represent data. Don't try to derive data from your UI layout.
e.g
commentList.map((comment) => (<Comment childCount={comment.children.length}));

Material UI - Chip Array

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>

showing only first element from data

Hi all I have following code: my code
In this scenario I am receiving some data from backend
const attachments = [
{
id: 1,
name: "someURLL_Name_1",
link: "https://someURLL_Name_1",
img: "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg"
},
{
id: 2,
name: "someURLL_Name_2",
link: "https://someURLL_Name_2",
img: "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg"
},
{
id: 3,
name: "someURL_Name_3",
link: "https://someURLL_Name_3",
img: "https://cdn.pixabay.com/photo/2015/04/23/22/00/tree-736885__340.jpg"
}
];
I need to map them all and show only first element form my data, and show with numbers rest hided data.
In the end it should be like something like this:
someURL_Name_1 https://someURLL_Name_1 +2 more
I successfully mapped all my data and write little logic for + more.
<div className={Styles.attachments}>
{data.map((item) => {
return <Attachment key={item.id} data={item} image={item.img} />;
})}
{data.length > 1 && (
<span className={Styles.more}>+{data.length - 1} more</span>
)}
</div>
Please help me to resolve a problem. Again, I want to show only first element , and then if there are another elements then I should hide them and show hide elements with numbers.
Thanks.
Just don't map over all entries then. The following will work :-
export const Attachments = ({ data }) => {
return (
<div className={Styles.attachments}>
{data[0] && (
<Attachment key={data[0].id} data={data[0]} image={data[0].img} />
)}
{data.length > 1 && (
<span className={Styles.more}>+{data.length - 1} more</span>
)}
</div>
);
};

React.js won't render properly when changing state for nested data

looking for some help here.
I'm implementing nested comment feature like reddit. Basically my data in 'state' look like this:
post: {
id: 'foo'
comments: [
{ id: '1', comments: [] },
{ id: '2', comments: [ { id: '3', comments: [...]}, ... ]}
]
}
My code to render the data:
<Row>
<Col md={12}>
{post.comments.map((comment, index) => {
return <Comment
key={comment.id}
comment={comment} />
})}
</Col>
</Row>
Comment.js:
render() {
const comment = this.props.comment;
return (
<div>
<Panel className="comment">
<Panel.Heading className="commentHeader">
<div>{comment.id}</div>
</Panel.Heading>
<Panel.Body>
<Row>
<h3>{comment.content}</h3>
{comment.comments &&
comment.comments.map((comment, index) => {
return <Comment key={comment.id}
comment={comment} />;
})}
</Row>
<Row>
<Button onClick={this.handleReply}> Reply </Button>
</Row>
</Panel.Body>
</Panel>
</div>
);
}
My problem is when I add outer comment in post react will render fine:
post: {
id: 'foo'
comments: [
{ id: '1', comments: [] },
{ id: '2', comments: [ { id: '3', comments: [...]}, ... ]},
{ id: 're-render works! see another comment', comments: []},
]
}
However, if the new 'state' look some thing like this, it will not re-render, but if I reload the page it would be fine:
post: {
id: 'foo',
comments: [
{ id: '1', comments: [{id: 'not working unless reload page',
comments: []}]},
{ id: '2', comments: [ { id: '3', comments: [...]}, ... ]},
{ id: 're-render works! see another comment', comments: []},
]
}
Thank you for your help!!!
I will try to offer you the solution:
You try to add the subcomments to the comments. So every comment has to have some id. You click add commment (child or nested) on the some (parent) comment. Then you get parent id and nested data and with some handleCommentAdd function do the following:
const handleCommentAdd = (id, nestedData) => {
this.setState(prevState => {
return {
...prevState,
post: {
...prevState.post,
comments: prevState.post.commments.map(comment => (
comment.id === id ?
{
...comment,
comments: comment.comments.concat(nestedData)
} : comment
))
}
}
})
}
Edit function to match to your state.
If you do not use arrow functions and spread operator:
const handleCommentAdd = function(id, nestedData) {
this.setState(function(prevState) {
return Object.assign(
{},
prevState,
{post: Object.assign(
{},
prevState.post,
{comments: prevState.post.comments.map(function(comment) {
comment.id === id ?
Object.assign(
{},
comment,
{comments: comment.comments.concat(nestedData)}
) : comment
})}
)}
)
})
}
After data update (existing, mounted) component receives new props - by default this (as opposite to state change) not results with rerender.
Assuming data is updated and change properly propagates (updated props in dev tools) ... you should use shouldComponentUpdate - return true on difference in prop.comment (shallow comparison should be enough) - thats all.

Categories